示例#1
0
 public override bool TryGetValue(UUID regionID, out RegionInfo rInfo)
 {
     if (m_Data.TryGetValue(regionID, out rInfo))
     {
         rInfo = new RegionInfo(rInfo);
         return(true);
     }
     return(false);
 }
示例#2
0
        public void Leave(UGI group, UUID sessionid, UGUI agent)
        {
            GroupSession session;

            if (m_ActiveSessions.TryGetValue(sessionid, out session) && group.EqualsGrid(session.Group))
            {
                session.Participants.Remove(agent);
                m_ActiveChats.RemoveIf(session.Group.ID, (r) => r.Participants.Count == 0);
            }
        }
        private double GetShoutDistance(UUID regionid)
        {
            double val;

            if (!m_ShoutDistances.TryGetValue(regionid, out val) &&
                !m_ShoutDistances.TryGetValue(UUID.Zero, out val))
            {
                val = 100;
            }
            return(val);
        }
        public override bool TryGetValue(UUID key, out AssetData asset)
        {
            AssetData internalAsset;

            if (m_Assets.TryGetValue(key, out internalAsset))
            {
                internalAsset.AccessTime = Date.Now;
                asset = new AssetData(internalAsset);
                return(true);
            }
            asset = null;
            return(false);
        }
        public void HandleConfirmXferPacket(Message m)
        {
            var req = (ConfirmXferPacket)m;
            DownloadTransferData tdata;

            if (m_DownloadTransfersByXferID.TryGetValue(req.ID, out tdata))
            {
                if (tdata.Packet != req.Packet || tdata.Position == 0)
                {
                    return;
                }

                var res = new SendXferPacket
                {
                    Packet = ++tdata.Packet
                };
                int remaininglength = tdata.Data.Length - tdata.Position;
                if (remaininglength > 1400)
                {
                    res.Data = new byte[1400];
                    Buffer.BlockCopy(tdata.Data, tdata.Position, res.Data, 0, 1400);
                    tdata.Position += 1400;
                }
                else
                {
                    res.Data = new byte[remaininglength];
                    Buffer.BlockCopy(tdata.Data, tdata.Position, res.Data, 0, remaininglength);
                    m_DownloadTransfersByXferID.Remove(req.ID);
                    m_DownloadTransfersByName.Remove(tdata.Filename);
                    res.Packet |= 0x80000000;
                }
                res.ID = tdata.XferID;
                SendMessageAlways(res, req.CircuitSceneID);
            }
        }
示例#6
0
        private Map ProcessJsonRequest(Map req)
        {
            Func <string, IValue, IValue> del;
            string method = req["method"].ToString();

            if (Json20RpcMethods.TryGetValue(method, out del))
            {
                try
                {
                    var res = new Map
                    {
                        { "jsonrpc", "2.0" }
                    };
                    try
                    {
                        res.Add("result", del(method, req["params"]));
                    }
                    catch (JSON20RpcException je)
                    {
                        return(FaultResponse(je.StatusCode, je.Message, req["id"].ToString()));
                    }
                    res.Add("id", req["id"]);
                    return(res);
                }
                catch (Exception e)
                {
                    m_Log.WarnFormat("Unexpected exception at XMRPC method {0}: {1}\n{2}", req["method"], e.GetType().Name, e.StackTrace);
                    return(FaultResponse(-32700, "Internal service error", req["id"].ToString()));
                }
            }
            else
            {
                return(FaultResponse(-32601, "Unknown Method", req["id"].ToString()));
            }
        }
示例#7
0
        public override bool TryGetValue(UEI experienceID, out ExperienceInfo experienceInfo)
        {
            ExperienceInfoCache info;

            if (m_ExperienceInfoCache.TryGetValue(experienceID.ID, out info) && m_ClockSource.TicksElapsed(m_ClockSource.TickCount, info.ExpiryTickCount) < m_ClockSource.SecsToTicks(ExperienceInfoTimeout))
            {
                experienceInfo = new ExperienceInfo(info.Info);
                return(true);
            }

            ExperienceInfo expInfo;

            if (InnerExperienceService.TryGetValue(experienceID, out expInfo))
            {
                info = new ExperienceInfoCache
                {
                    Info            = expInfo,
                    ExpiryTickCount = m_ClockSource.TickCount
                };
                m_ExperienceInfoCache[experienceID.ID] = info;
                experienceInfo = new ExperienceInfo(expInfo);
                return(true);
            }

            experienceInfo = default(ExperienceInfo);
            return(false);
        }
        public static ResourceSet GetLanguageResourceSet(
            this object o,
            CultureInfo selectedCulture)
        {
            var         type         = (o is Type) ? (Type)o : o.GetType();
            var         assembly     = type.Assembly;
            var         assemblyName = assembly.GetName().Name;
            ResourceSet res          = null;
            var         culture      = selectedCulture ?? EnUsCulture;
            var         cultureName  = culture.Name;
            var         cultureGroup = cultureName.Split('-')[0];

            if (!m_LoadedAssemblyResources.TryGetValue(cultureName + ":" + assemblyName, out res) &&
                !m_LoadedAssemblyResources.TryGetValue(cultureGroup + ":" + assemblyName, out res))
            {
                lock (m_LoadAssemblyLock)
                {
                    if (!m_LoadedAssemblyResources.TryGetValue(cultureName + ":" + assemblyName, out res))
                    {
                        var fName = Path.Combine(InstallBinPath, "languages/" + cultureName + "/" + assemblyName + "." + cultureName + ".resources");
                        if (File.Exists(fName))
                        {
                            using (var reader = new ResourceReader(fName))
                            {
                                res = new ResourceSet(reader);
                                m_LoadedAssemblyResources.Add(cultureName + ":" + assemblyName, res);
                            }
                        }
                    }
                    else if (cultureGroup != cultureName &&
                             !m_LoadedAssemblyResources.TryGetValue(cultureGroup + ":" + assemblyName, out res))
                    {
                        var fName = Path.Combine(InstallBinPath, "languages/" + cultureGroup + "/" + assemblyName + "." + cultureGroup + ".resources");
                        if (File.Exists(fName))
                        {
                            using (var reader = new ResourceReader(fName))
                            {
                                res = new ResourceSet(reader);
                                m_LoadedAssemblyResources.Add(cultureGroup + ":" + assemblyName, res);
                            }
                        }
                    }
                }
            }

            return(res);
        }
        private void HandleClassifiedInfoRequest(ViewerAgent agent, SceneInterface scene, Message m)
        {
            var req = (ClassifiedInfoRequest)m;

            if (req.AgentID != req.CircuitAgentID ||
                req.SessionID != req.CircuitSessionID)
            {
                return;
            }

            KeyValuePair <UGUI, int> kvp;

            if (!m_ClassifiedQueryCache.TryGetValue(req.ClassifiedID, out kvp))
            {
                return;
            }

            ProfileServiceData serviceData;
            UGUI uui;

            try
            {
                serviceData = LookupProfileService(scene, kvp.Key.ID, out uui);
            }
            catch
            {
                return;
            }

            try
            {
                ProfileClassified cls = serviceData.ProfileService.Classifieds[kvp.Key, req.ClassifiedID];
                var reply             = new ClassifiedInfoReply
                {
                    AgentID = req.AgentID,

                    ClassifiedID    = cls.ClassifiedID,
                    CreatorID       = cls.Creator.ID,
                    CreationDate    = cls.CreationDate,
                    ExpirationDate  = cls.ExpirationDate,
                    Category        = cls.Category,
                    Name            = cls.Name,
                    Description     = cls.Description,
                    ParcelID        = cls.ParcelID,
                    ParentEstate    = cls.ParentEstate,
                    SnapshotID      = cls.SnapshotID,
                    SimName         = cls.SimName,
                    PosGlobal       = cls.GlobalPos,
                    ParcelName      = cls.ParcelName,
                    ClassifiedFlags = cls.Flags,
                    PriceForListing = cls.Price
                };
                agent.SendMessageAlways(reply, scene.ID);
            }
            catch
            {
                /* do not expose exceptions to caller */
            }
        }
        private bool TryGetExperienceService(UUID experienceID, out ExperienceServiceInterface experienceService)
        {
            UEI experience;
            ExperienceBrokerEntry entry;

            if (m_NameCache.TryGetValue(experienceID, out entry) && m_ClockSource.TicksElapsed(m_ClockSource.TickCount, entry.ExpiryTickCount) < m_ClockSource.SecsToTicks(120))
            {
                experienceService = entry;
                return(true);
            }
            if (!m_ExperienceNameService.TryGetValue(experienceID, out experience))
            {
                experienceService = default(ExperienceServiceInterface);
                return(false);
            }
            return(TryGetExperienceService(experience, out experienceService));
        }
        private bool TryGetConvexShapeFromMesh(PrimitivePhysicsShapeType physicsShape, ObjectPart.PrimitiveShape shape, out PhysicsShapeReference physicshaperef)
        {
            PhysicsConvexShape    physicshape;
            PhysicsShapeReference physicshaperes = null;
            UUID meshId = shape.SculptMap;
            bool s      = m_Lock.AcquireReaderLock(() =>
            {
                if (m_ConvexShapesBySculptMesh.TryGetValue(GetMeshKey(meshId, physicsShape), out physicshape))
                {
                    physicshaperes = new PhysicsShapeMeshReference(meshId, physicsShape, this, physicshape);
                    return(true);
                }
                return(false);
            });

            if (s)
            {
                physicshaperef = physicshaperes;
                return(true);
            }

            if (m_DisableCache || !m_SimulationStorage.PhysicsConvexShapes.TryGetValue(meshId, physicsShape, out physicshape))
            {
                /* we may produce additional meshes sometimes but it is better not to lock while generating the mesh */
                physicshape = ConvertToMesh(physicsShape, shape);
                if (physicshape == null)
                {
                    physicshaperef = null;
                    return(false);
                }
                foreach (PhysicsConvexShape.ConvexHull hull in physicshape.Hulls)
                {
                    if (hull.Vertices.Count == 0)
                    {
                        m_Log.WarnFormat("Physics shape of mesh generated a 0 point hull: {0} / {1}", physicsShape, meshId);
                        physicshaperef = null;
                        return(false);
                    }
                }
                m_SimulationStorage.PhysicsConvexShapes[meshId, physicsShape] = physicshape;
            }

            /* we only lock out the decrement use count here */
            physicshaperef = m_Lock.AcquireReaderLock(() =>
            {
                try
                {
                    m_ConvexShapesBySculptMesh.Add(GetMeshKey(meshId, physicsShape), physicshape);
                }
                catch
                {
                    physicshape = m_ConvexShapesBySculptMesh[GetMeshKey(meshId, physicsShape)];
                }
                return(new PhysicsShapeMeshReference(meshId, physicsShape, this, physicshape));
            });

            return(true);
        }
示例#12
0
        public ObjectPartLocalizedInfo GetLocalization(CultureInfo culture)
        {
            ObjectPartLocalizedInfo info;

            if (m_NamedLocalizations.Count == 0)
            {
                /* no detail check */
            }
            else if (m_NamedLocalizations.TryGetValue(culture.ToString(), out info))
            {
                return(info);
            }
            else if (m_NamedLocalizations.TryGetValue(culture.TwoLetterISOLanguageName, out info))
            {
                return(info);
            }
            return(m_DefaultLocalization);
        }
示例#13
0
        public void HttpRequestHandler(HttpRequest httpreq)
        {
            if (httpreq.CallerIP != m_RemoteIP)
            {
                httpreq.ErrorResponse(HttpStatusCode.Forbidden, "Forbidden");
                return;
            }
            if (httpreq.Method != "POST")
            {
                httpreq.ErrorResponse(HttpStatusCode.MethodNotAllowed, "Method not allowed");
                return;
            }

            Map reqmap;

            try
            {
                reqmap = LlsdXml.Deserialize(httpreq.Body) as Map;
            }
            catch
            {
                httpreq.ErrorResponse(HttpStatusCode.UnsupportedMediaType, "Unsupported Media Type");
                return;
            }
            if (reqmap == null)
            {
                httpreq.ErrorResponse(HttpStatusCode.BadRequest, "Misformatted LLSD-XML");
                return;
            }

            string method;

            if (!reqmap.TryGetValue("method", out method))
            {
                httpreq.ErrorResponse(HttpStatusCode.BadRequest, "Unknown method");
                return;
            }

            Func <ViewerAgent, AgentCircuit, HttpRequest, Map, Map> del;

            if (!ChatSessionRequestMethodHandlers.TryGetValue(method, out del))
            {
                httpreq.ErrorResponse(HttpStatusCode.BadRequest, "Unknown method");
                return;
            }

            Map resdata = del(m_Agent, m_Circuit, httpreq, reqmap);

            using (HttpResponse httpres = httpreq.BeginResponse())
            {
                httpres.ContentType = "application/llsd+xml";
                using (Stream outStream = httpres.GetOutputStream())
                {
                    LlsdXml.Serialize(resdata, outStream);
                }
            }
        }
示例#14
0
 public override bool TryGetValue(UUID groupID, out UGI ugi)
 {
     if(m_Data.TryGetValue(groupID, out ugi))
     {
         ugi = new UGI(ugi);
         return true;
     }
     ugi = default(UGI);
     return false;
 }
 bool ISimulationDataRegionSettingsStorageInterface.TryGetValue(UUID regionID, out RegionSettings settings)
 {
     if (m_RegionSettingsData.TryGetValue(regionID, out settings))
     {
         settings = new RegionSettings(settings);
         return(true);
     }
     settings = null;
     return(false);
 }
        public override bool Remove(UGUI requestingAgent, UEI id)
        {
            ExperienceInfo info;

            if (!m_Experiences.TryGetValue(id.ID, out info))
            {
                return(false);
            }
            if (!requestingAgent.EqualsGrid(info.Owner))
            {
                return(false);
            }

            bool f = m_Experiences.Remove(id.ID);

            m_Perms.Remove(id);
            m_KeyValues.Remove(id);
            m_Admins.Remove(id);
            return(f);
        }
示例#17
0
        private bool TryGetGroupsService(UUID groupID, out GroupsServiceInterface groupsService)
        {
            UGI group;
            GroupsBrokerEntry entry;

            if (m_NameCache.TryGetValue(groupID, out entry) && m_ClockSource.TicksElapsed(m_ClockSource.TickCount, entry.ExpiryTickCount) < m_ClockSource.SecsToTicks(120))
            {
                groupsService = entry;
                return(true);
            }
            if (!m_GroupsNameService.TryGetValue(groupID, out group))
            {
                groupsService = default(GroupsServiceInterface);
                return(false);
            }
            bool result = TryGetGroupsService(group, out entry);

            groupsService = entry;
            return(result);
        }
示例#18
0
        private void HandleFetchInventoryReply(Message m)
        {
            var reply = (FetchInventoryReply)m;
            FetchInventoryListener listener;

            if (m_FetchInventoryReply.TryGetValue(reply.ItemData[0].ItemID, out listener))
            {
                listener.Reply = reply;
                listener.Event.Set();
            }
        }
示例#19
0
        List <Vector3> ISimulationDataSpawnPointStorageInterface.this[UUID regionID]
        {
            get
            {
                RwLockedList <Vector3> data;
                return((m_SpawnPointData.TryGetValue(regionID, out data)) ?
                       new List <Vector3>(data) :
                       new List <Vector3>());
            }

            set { m_SpawnPointData[regionID] = new RwLockedList <Vector3>(value); }
        }
        private void HandleImageData(Message m, ViewerConnection vc, ViewerAgentAccessor agent)
        {
            ImageData        res = (ImageData)m;
            ImageReceiveInfo info;

            if (m_ActiveImageTransfers.TryGetValue(res.ID, out info))
            {
                info.FirstPacket = res;
                if (info.IsComplete)
                {
                    vc.PostEvent(new TextureReceivedEvent
                    {
                        Agent     = agent,
                        TextureID = res.ID,
                        Success   = 1,
                        Data      = new ByteArrayApi.ByteArray(info.Data)
                    });
                    m_ActiveImageTransfers.Remove(res.ID);
                }
            }
        }
示例#21
0
        public override void Send(ListenEvent ev)
        {
            ChannelInfo ci;

            if (m_Channels.TryGetValue(ev.Channel, out ci))
            {
                ci.Send(ev);
            }
            if (ev.Channel != ListenEvent.DEBUG_CHANNEL)
            {
                switch (ev.Type)
                {
                case ListenEvent.ChatType.Say:
                    ev.Distance = SayDistance;
                    break;

                case ListenEvent.ChatType.Shout:
                    ev.Distance = ShoutDistance;
                    break;

                case ListenEvent.ChatType.Whisper:
                    ev.Distance = WhisperDistance;
                    break;

                case ListenEvent.ChatType.Region:
                    break;

                default:
                    return;
                }
                if (ev.SourceType == ListenEvent.ChatSourceType.Object &&
                    ev.OriginSceneID == UUID.Zero)
                {
                    foreach (Listener li in m_ChatPass)
                    {
                        li.Send(ev);
                    }
                }
            }
        }
        List <GroupMember> IGroupMembersInterface.this[UGUI requestingAgent, UGI group]
        {
            get
            {
                GroupMemberCache cache;
                UGUI_UGI         cacheId = new UGUI_UGI {
                    RequestingAgent = requestingAgent, Group = group
                };
                if (m_GroupMemberCache.TryGetValue(cacheId, out cache) && m_ClockSource.TicksElapsed(m_ClockSource.TickCount, cache.ExpiryTickCount) < m_ClockSource.SecsToTicks(MemberCacheTimeout))
                {
                    return(cache.GroupMembers);
                }

                cache = new GroupMemberCache
                {
                    GroupMembers = InnerGroupsService.Members[requestingAgent, group]
                };
                cache.ExpiryTickCount       = m_ClockSource.TickCount;
                m_GroupMemberCache[cacheId] = cache;
                return(cache.GroupMembers);
            }
        }
示例#23
0
 bool ISimulationDataEnvSettingsStorageInterface.TryGetValue(UUID regionID, out WindLightSettings settings)
 {
     byte[] data;
     if (m_EnvSettingsData.TryGetValue(regionID, out data))
     {
         using (var ms = new MemoryStream(data))
         {
             settings = WindLightSettings.Deserialize(ms);
             return(true);
         }
     }
     settings = null;
     return(false);
 }
        public override bool TryGetValue(uint estateID, out EstateInfo estateInfo)
        {
            EstateInfo intern;

            if (m_Data.TryGetValue(estateID, out intern))
            {
                lock (intern)
                {
                    estateInfo = new EstateInfo(intern);
                }
                return(true);
            }
            estateInfo = default(EstateInfo);
            return(false);
        }
        private void UpdateWindlightProfileToClients()
        {
            var m = (m_WindlightValid) ?
                    CompileWindlightSettings(m_SkyWindlight, m_WaterWindlight) :
                    CompileResetWindlightSettings();

            foreach (IAgent agent in m_Scene.Agents)
            {
                bool overrideLs;
                if (m_OverrideLightSharePerAgent.TryGetValue(agent.Owner.ID, out overrideLs) && overrideLs)
                {
                    continue;
                }
                agent.SendMessageIfRootAgent(m, m_Scene.ID);
            }
        }
示例#26
0
        public bool TryGetDestination(GridVector gv, out RegionInfo di)
        {
            KeyValuePair <ulong, RegionInfo> dest;

            di = default(RegionInfo);
            if (m_InterGridDestinations.TryGetValue(gv.RegionHandle, out dest) &&
                Date.GetUnixTime() - dest.Key <= 240)
            {
                di = dest.Value;
                return(true);
            }

            CleanDestinationCache();

            return(false);
        }
示例#27
0
        bool IExperienceKeyValueInterface.StoreOnlyIfEqualOrig(UEI experienceID, string key, string value, string orig_value)
        {
            bool changed;
            RwLockedDictionary <string, string> exp = m_KeyValues[experienceID];

            lock (m_UpdateLock)
            {
                string ov;
                changed = exp.TryGetValue(key, out ov) && ov == orig_value;
                if (changed)
                {
                    exp[key] = value;
                }
            }

            return(changed);
        }
示例#28
0
        bool ISimulationDataLightShareStorageInterface.TryGetValue(UUID regionID, out EnvController.WindlightSkyData skyData, out EnvController.WindlightWaterData waterData)
        {
            KeyValuePair <EnvController.WindlightSkyData, EnvController.WindlightWaterData> kvp;

            if (m_LightShareData.TryGetValue(regionID, out kvp))
            {
                skyData   = kvp.Key;
                waterData = kvp.Value;
                return(true);
            }
            else
            {
                skyData   = EnvController.WindlightSkyData.Defaults;
                waterData = EnvController.WindlightWaterData.Defaults;
                return(false);
            }
        }
示例#29
0
        /*
         * public IList<TerrainPatch> GetTerrainDistanceSorted(Vector3 v)
         * {
         *  SortedList<int, TerrainPatch> sorted = new SortedList<int, TerrainPatch>();
         *  uint x;
         *  uint y;
         *
         *  if(v.X < 0)
         *  {
         *      x = 0;
         *  }
         *  else if(v.X >= SizeX)
         *  {
         *      x = SizeX - 1;
         *  }
         *  else
         *  {
         *      x = (uint)v.X / TERRAIN_PATCH_SIZE;
         *  }
         *
         *  if (v.Y < 0)
         *  {
         *      y = 0;
         *  }
         *  else if(v.Y >= SizeY)
         *  {
         *      y = SizeY - 1;
         *  }
         *  else
         *  {
         *      y = (uint)v.Y / TERRAIN_PATCH_SIZE;
         *  }
         *
         *  int distance;
         *
         *  for(uint py = 0; py < SizeY / TERRAIN_PATCH_SIZE; ++py)
         *  {
         *      for(uint px = 0; px < SizeX / TERRAIN_PATCH_SIZE; ++px)
         *      {
         *          distance = ((int)px - (int)x) * ((int)px - (int)x) + ((int)py - (int)y) * ((int)py - (int)y);
         *          sorted.Add(distance, new TerrainPatch(px, py, m_Map[py * m_PatchCountX + px]));
         *      }
         *  }
         *
         *  return sorted.Values;
         * }
         */
        private List <LayerData> CompileTerrainData(IAgent agent) => m_TerrainRwLock.AcquireReaderLock(() =>
        {
            int y;
            int x;
            var mlist        = new List <LayerData>();
            var dirtyPatches = new List <LayerPatch>();
            RwLockedDictionary <uint, uint> agentSceneSerials = agent.TransmittedTerrainSerials[m_Scene.ID];

            for (y = 0; y < m_Scene.SizeY / LayerCompressor.LAYER_PATCH_NUM_XY_ENTRIES; ++y)
            {
                for (x = 0; x < m_Scene.SizeX / LayerCompressor.LAYER_PATCH_NUM_XY_ENTRIES; ++x)
                {
                    LayerPatch patch = m_TerrainPatches[y, x];
                    uint serial;
                    if (agentSceneSerials.TryGetValue(patch.ExtendedPatchID, out serial))
                    {
                        if (serial != patch.Serial)
                        {
                            agentSceneSerials[patch.ExtendedPatchID] = serial;
                            dirtyPatches.Add(m_TerrainPatches[y, x]);
                        }
                    }
                    else
                    {
                        dirtyPatches.Add(m_TerrainPatches[y, x]);
                    }
                }
            }
            var layerType = LayerData.LayerDataType.Land;

            if (BASE_REGION_SIZE < m_Scene.SizeX || BASE_REGION_SIZE < m_Scene.SizeY)
            {
                layerType = LayerData.LayerDataType.LandExtended;
            }
            int offset = 0;
            while (offset < dirtyPatches.Count)
            {
                int remaining  = dirtyPatches.Count - offset;
                int actualused = 0;
                mlist.Add(LayerCompressor.ToLayerMessage(dirtyPatches, layerType, offset, remaining, out actualused));
                offset += actualused;
            }
            return(mlist);
        });
示例#30
0
        private void CacheFriends()
        {
            lock (m_KnownFriendsCacheLock)
            {
                if (!m_KnownFriendsCached)
                {
                    if (FriendsService != null)
                    {
                        if (m_KnownFriends.Count == 0)
                        {
                            foreach (FriendInfo fi in FriendsService[Owner])
                            {
                                m_KnownFriends.Add(fi.Friend.ID, new FriendStatus(fi));
                            }
                        }
                    }
                    else
                    {
                        /* if we have already some entries, we keep the ones that are still valid */
                        var haveIDs = new List <UUID>(m_KnownFriends.Keys);
                        foreach (var fi in FriendsService[Owner])
                        {
                            FriendStatus fStat;
                            if (m_KnownFriends.TryGetValue(fi.Friend.ID, out fStat))
                            {
                                fStat.FriendGivenFlags = fi.FriendGivenFlags;
                                fStat.UserGivenFlags   = fi.UserGivenFlags;
                            }
                            else
                            {
                                m_KnownFriends.Add(fi.Friend.ID, new FriendStatus(fi));
                            }
                            haveIDs.Remove(fi.Friend.ID);
                        }

                        foreach (var id in haveIDs)
                        {
                            m_KnownFriends.Remove(id);
                        }
                    }
                    m_KnownFriendsCached = true;
                }
            }
        }