From c8b88141cb7c689304f7666db483d6eace6e7311 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 14 Mar 2025 04:38:03 +0000 Subject: [PATCH] change picks update/add, enforcing max number of picks --- .../Avatar/UserProfiles/UserProfileModule.cs | 257 ++++++++++++++---- 1 file changed, 208 insertions(+), 49 deletions(-) diff --git a/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs index 2d3aff66c9..0b844edc83 100644 --- a/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs +++ b/OpenSim/Region/CoreModules/Avatar/UserProfiles/UserProfileModule.cs @@ -929,6 +929,57 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles remoteClient.SendAvatarPicksReply(targetId, picks); } + public Dictionary GetPicks(UUID targetId) + { + Dictionary picks = new(); + + UserProfileCacheEntry uce = null; + lock(m_profilesCache) + { + if(m_profilesCache.TryGetValue(targetId, out uce) && uce is not null) + { + if(uce.picksList is not null) + return uce.picksList; + } + } + + GetUserProfileServerURI(targetId, out string serverURI); + if(string.IsNullOrWhiteSpace(serverURI)) + return picks; + + OSDMap parameters= new() + { + {"creatorId", OSD.FromUUID(targetId)} + }; + OSD osdtmp = parameters; + if(!rpc.JsonRpcRequest(ref osdtmp, "avatarpicksrequest", serverURI, UUID.Random().ToString())) + return picks; + + parameters = (OSDMap)osdtmp; + if(!parameters.TryGetValue("result", out osdtmp) || osdtmp is not OSDArray) + return picks; + + OSDArray list = (OSDArray)osdtmp; + foreach(OSD map in list) + { + OSDMap m = (OSDMap)map; + UUID cid = m["pickuuid"].AsUUID(); + string name = m["name"].AsString(); + picks[cid] = name; + } + + lock(m_profilesCache) + { + if(!m_profilesCache.TryGetValue(targetId, out uce) || uce is null) + uce = new UserProfileCacheEntry(); + uce.picksList = picks; + + m_profilesCache.AddOrUpdate(targetId, uce, PROFILECACHEEXPIRE); + } + + return picks; + } + /// /// Handles the pick info request. /// @@ -946,13 +997,10 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles if (sender is not IClientAPI) return; - UserProfilePick pick = new(); if(!UUID.TryParse(args [0], out UUID targetID)) return; - pick.CreatorId = targetID; - - if(!UUID.TryParse (args [1], out pick.PickId)) + if(!UUID.TryParse (args [1], out UUID PickId)) return; IClientAPI remoteClient = (IClientAPI)sender; @@ -961,13 +1009,12 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles { if(m_profilesCache.TryGetValue(targetID, out uce) && uce is not null) { - if(uce.picks is not null && uce.picks.ContainsKey(pick.PickId)) + if(uce.picks is not null && uce.picks.TryGetValue(PickId, out UserProfilePick cpick)) { - pick = uce.picks[pick.PickId]; - if(Vector3d.TryParse(pick.GlobalPos, out Vector3d gPos)) - remoteClient.SendPickInfoReply(pick.PickId,pick.CreatorId,pick.TopPick,pick.ParcelId,pick.Name, - pick.Desc,pick.SnapshotId,pick.ParcelName,pick.OriginalName,pick.SimName, - gPos,pick.SortOrder,pick.Enabled); + if(Vector3d.TryParse(cpick.GlobalPos, out Vector3d gPos)) + remoteClient.SendPickInfoReply(cpick.PickId,cpick.CreatorId,cpick.TopPick,cpick.ParcelId,cpick.Name, + cpick.Desc,cpick.SnapshotId,cpick.ParcelName,cpick.OriginalName,cpick.SimName, + gPos,cpick.SortOrder,cpick.Enabled); return; } } @@ -975,9 +1022,13 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles bool foreign = GetUserProfileServerURI (targetID, out string serverURI); if(string.IsNullOrWhiteSpace(serverURI)) - { return; - } + + UserProfilePick pick = new() + { + PickId = PickId, + CreatorId = targetID + }; object Pick = (object)pick; if (!rpc.JsonRpcRequest (ref Pick, "pickinforequest", serverURI, UUID.Random ().ToString ())) { @@ -1047,6 +1098,79 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles globalPos,pick.SortOrder,pick.Enabled); } + public UserProfilePick GetPick(UUID targetID, UUID PickId) + { + lock(m_profilesCache) + { + if(m_profilesCache.TryGetValue(targetID, out UserProfileCacheEntry uce) && uce is not null) + { + if(uce.picks is not null && uce.picks.TryGetValue(PickId, out UserProfilePick cpick)) + return cpick; + } + } + + UserProfilePick pick = new() + { + PickId = PickId, + CreatorId = targetID + }; + + bool foreign = GetUserProfileServerURI (targetID, out string serverURI); + if(string.IsNullOrWhiteSpace(serverURI)) + return null; + + object Pick = (object)pick; + if (!rpc.JsonRpcRequest (ref Pick, "pickinforequest", serverURI, UUID.Random ().ToString ())) { + return null; + } + + pick = (UserProfilePick)Pick; + if(foreign) + cacheForeignImage(targetID, pick.SnapshotId); + + if(!Vector3d.TryParse(pick.GlobalPos, out Vector3d globalPos)) + return null; + + if (m_thisGridInfo.IsLocalGrid(pick.Gatekeeper, true) == 0) + { + // Setup the illusion + string region = string.Format("{0} {1}",pick.Gatekeeper,pick.SimName); + GridRegion target = Scene.GridService.GetRegionByName(Scene.RegionInfo.ScopeID, region); + + if(target is null) + { + // This is a unreachable region + } + else + { + // we have a proxy on map + if (Util.ParseFakeParcelID(pick.ParcelId, out ulong _, out uint oriX, out uint 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); + + pick.ParcelId = Util.BuildFakeParcelID(target.RegionHandle, (uint)globalPos.X, (uint)globalPos.Y); + + globalPos.X += target.RegionLocX; + globalPos.Y += target.RegionLocY; + pick.GlobalPos = globalPos.ToString(); + } + } + } + return pick; + } + /// /// Updates the userpicks /// @@ -1081,73 +1205,108 @@ namespace OpenSim.Region.CoreModules.Avatar.UserProfiles { //m_log.DebugFormat("[PROFILES]: Start PickInfoUpdate Name: {0} PickId: {1} SnapshotId: {2}", name, pickID.ToString(), snapshotID.ToString()); - UserProfilePick pick = new(); GetUserProfileServerURI(remoteClient.AgentId, out string serverURI); if(string.IsNullOrWhiteSpace(serverURI)) return; ScenePresence p = FindPresence(remoteClient.AgentId); + if(p is null) + return; - Vector3 avaPos = p.AbsolutePosition; - // Getting the global position for the Avatar - Vector3d posGlobal = new(remoteClient.Scene.RegionInfo.WorldLocX + avaPos.X, - remoteClient.Scene.RegionInfo.WorldLocY + avaPos.Y, - avaPos.Z); + if(remoteClient.AgentId.NotEqual(creatorID) && !p.IsViewerUIGod) + return; - string landParcelName = "My Parcel"; + UserProfilePick pick = null; + Dictionary curpicks = GetPicks(creatorID); + if(!curpicks.ContainsKey(pickID)) + { + if(curpicks is not null && curpicks.Count >= Constants.MaxProfilePicks) + { + remoteClient.SendAvatarPicksReply(remoteClient.AgentId, curpicks); + return; + } + } + else + pick = GetPick(creatorID, pickID); - // to locate parcels we use a fake id that encodes the region handle - // since we do not have a global locator - // this fails on HG - UUID landParcelID = Util.BuildFakeParcelID(remoteClient.Scene.RegionInfo.RegionHandle, (uint)avaPos.X, (uint)avaPos.Y); - ILandObject land = p.Scene.LandChannel.GetLandObject(avaPos.X, avaPos.Y); + Vector3d posGlobal; - if (land is not null) + if(pick is null) { - // If land found, use parcel uuid from here because the value from SP will be blank if the avatar hasnt moved - landParcelName = land.LandData.Name; + Vector3 avaPos = p.AbsolutePosition; + // Getting the global position for the Avatar + posGlobal = new(remoteClient.Scene.RegionInfo.WorldLocX + avaPos.X, + remoteClient.Scene.RegionInfo.WorldLocY + avaPos.Y, + avaPos.Z); + + string landParcelName = "My Parcel"; + + // to locate parcels we use a fake id that encodes the region handle + // since we do not have a global locator + // this fails on HG + UUID landParcelID = Util.BuildFakeParcelID(remoteClient.Scene.RegionInfo.RegionHandle, (uint)avaPos.X, (uint)avaPos.Y); + ILandObject land = p.Scene.LandChannel.GetLandObject(avaPos.X, avaPos.Y); + + if (land is not null) + { + // If land found, use parcel uuid from here because the value from SP will be blank if the avatar hasnt moved + landParcelName = land.LandData.Name; + } + else + { + m_log.WarnFormat( + "[PROFILES]: PickInfoUpdate found no parcel info at {0},{1} in {2}", + avaPos.X, avaPos.Y, p.Scene.Name); + } + + pick = new() + { + PickId = pickID, + CreatorId = creatorID, + TopPick = topPick, + Name = name, + Desc = desc, + ParcelId = landParcelID, + SnapshotId = snapshotID, + ParcelName = landParcelName, + SimName = remoteClient.Scene.RegionInfo.RegionName, + Gatekeeper = m_thisGridInfo.GateKeeperURLNoEndSlash, + GlobalPos = posGlobal.ToString(), + SortOrder = sortOrder, + Enabled = enabled + }; } else { - m_log.WarnFormat( - "[PROFILES]: PickInfoUpdate found no parcel info at {0},{1} in {2}", - avaPos.X, avaPos.Y, p.Scene.Name); + pick.TopPick = topPick; + pick.Name = name; + pick.Desc = desc; + pick.SnapshotId = snapshotID; + pick.SortOrder = sortOrder; + pick.Enabled = enabled; } - pick.PickId = pickID; - pick.CreatorId = creatorID; - pick.TopPick = topPick; - pick.Name = name; - pick.Desc = desc; - pick.ParcelId = landParcelID; - pick.SnapshotId = snapshotID; - pick.ParcelName = landParcelName; - pick.SimName = remoteClient.Scene.RegionInfo.RegionName; - pick.Gatekeeper = m_thisGridInfo.GateKeeperURLNoEndSlash; - pick.GlobalPos = posGlobal.ToString(); - pick.SortOrder = sortOrder; - pick.Enabled = enabled; - object Pick = (object)pick; if(!rpc.JsonRpcRequest(ref Pick, "picks_update", serverURI, UUID.Random().ToString())) { - remoteClient.SendAgentAlertMessage( - "Error updating pick", false); + remoteClient.SendAgentAlertMessage("Error updating pick", false); return; } UserProfileCacheEntry uce = null; lock(m_profilesCache) { - if(!m_profilesCache.TryGetValue(remoteClient.AgentId, out uce) || uce is null) + if(!m_profilesCache.TryGetValue(creatorID, out uce) || uce is null) uce = new UserProfileCacheEntry(); uce.picks ??= new Dictionary(); uce.picksList ??= new Dictionary(); uce.picks[pick.PickId] = pick; uce.picksList[pick.PickId] = pick.Name; - m_profilesCache.AddOrUpdate(remoteClient.AgentId, uce, PROFILECACHEEXPIRE); + m_profilesCache.AddOrUpdate(creatorID, uce, PROFILECACHEEXPIRE); } - remoteClient.SendAvatarPicksReply(remoteClient.AgentId, uce.picksList); + + _ = Vector3d.TryParse(pick.GlobalPos, out posGlobal); + remoteClient.SendAvatarPicksReply(creatorID, uce.picksList); remoteClient.SendPickInfoReply(pick.PickId,pick.CreatorId,pick.TopPick,pick.ParcelId,pick.Name, pick.Desc,pick.SnapshotId,pick.ParcelName,pick.OriginalName,pick.SimName, posGlobal,pick.SortOrder,pick.Enabled);