<commit_before><commit_msg>Forgot to add AvatarManager.cs in my previous commit<commit_after>/*
 * Copyright (c) 2006, Second Life Reverse Engineering Team
 * All rights reserved.
 *
 * - 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.
 * - Neither the name of the Second Life Reverse Engineering Team 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 COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR 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.Threading;
using libsecondlife.Packets;

namespace libsecondlife
{
    /// <summary>
    /// Triggered after friend request packet is sent out
    /// </summary>
    /// <param name="AgentID"></param>
    /// <param name="Online"></param>
    public delegate void FriendNotificationCallback(LLUUID agentID, bool online);

    /// <summary>
    /// Triggered when a UUIDNameReply is received
    /// </summary>
    /// <param name="names"></param>
    public delegate void AgentNamesCallback(Dictionary<LLUUID, string> names);

    /// <summary>
    /// 
    /// </summary>
    public class AvatarManager
    {
        /// <summary>Triggered whenever a friend comes online or goes offline</summary>
        public event FriendNotificationCallback OnFriendNotification;

        private SecondLife Client;
        private Dictionary<LLUUID, Avatar> Avatars;
        private Mutex AvatarsMutex;
        private AgentNamesCallback OnAgentNames;

        public AvatarManager(SecondLife client)
        {
            Client = client;
            Avatars = new Dictionary<LLUUID, Avatar>();
            AvatarsMutex = new Mutex(false, "AvatarsMutex");

            // Friend notification callback
            PacketCallback callback = new PacketCallback(FriendNotificationHandler);
            Client.Network.RegisterCallback(PacketType.OnlineNotification, callback);
            Client.Network.RegisterCallback(PacketType.OfflineNotification, callback);
            Client.Network.RegisterCallback(PacketType.UUIDNameReply, new PacketCallback(GetAgentNameHandler));
        }

        /// <summary>
        /// Add an Avatar into the Avatars Dictionary
        /// </summary>
        /// <param name="avatar">Filled-out Avatar class to insert</param>
        public void AddAvatar(Avatar avatar)
        {
            #region AvatarsMutex
            AvatarsMutex.WaitOne();

            Avatars[avatar.ID] = avatar;

            AvatarsMutex.ReleaseMutex();
            #endregion AvatarsMutex
        }

        public bool Contains(LLUUID id)
        {
            #region AvatarsMutex
            AvatarsMutex.WaitOne();

            bool contains = Avatars.ContainsKey(id);

            #endregion AvatarsMutex
            AvatarsMutex.ReleaseMutex();

            return contains;
        }

        /// <summary>
        /// This function will only check if the avatar name exists locally,
        /// it will not do any networking calls to fetch the name
        /// </summary>
        /// <returns>The avatar name, or an empty string if it's not found</returns>
        public string LocalAvatarNameLookup(LLUUID id)
        {
            string name;

            #region AvatarsMutex
            AvatarsMutex.WaitOne();

            if (Avatars.ContainsKey(id))
            {
                name = Avatars[id].Name;
            }
            else
            {
                name = "";
            }

            #endregion AvatarsMutex
            AvatarsMutex.ReleaseMutex();

            return name;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="id"></param>
        public void BeginGetAvatarName(LLUUID id, AgentNamesCallback anc)
        {
            // TODO: BeginGetAvatarNames is pretty bulky, rewrite a simple version here

            List<LLUUID> ids = new List<LLUUID>();
            ids.Add(id);

            BeginGetAvatarNames(ids, anc);
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="ids"></param>
        public void BeginGetAvatarNames(List<LLUUID> ids, AgentNamesCallback anc)
        {
            if (anc != null)
            {
                OnAgentNames = anc;
            }

            Dictionary<LLUUID, string> havenames = new Dictionary<LLUUID,string>();
            List<LLUUID> neednames = new List<LLUUID>();

            // Fire callbacks for the ones we already have cached
            foreach (LLUUID id in ids)
            {
                if (Avatars.ContainsKey(id))
                {
                    havenames[id] = Avatars[id].Name;
                }
                else
                {
                    neednames.Add(id);
                }
            }

            if (havenames.Count > 0)
            {
                OnAgentNames(havenames);
            }

            if (neednames.Count > 0)
            {
                UUIDNameRequestPacket request = new UUIDNameRequestPacket();

                request.UUIDNameBlock = new UUIDNameRequestPacket.UUIDNameBlockBlock[neednames.Count];

                for (int i = 0; i < neednames.Count; i++)
                {
                    request.UUIDNameBlock[i] = new UUIDNameRequestPacket.UUIDNameBlockBlock();
                    request.UUIDNameBlock[i].ID = neednames[i];
                }

                Client.Network.SendPacket(request);
            }
        }

        /// <summary>
        /// Process an incoming UUIDNameReply Packet and insert Full Names into the Avatars Dictionary
        /// </summary>
        /// <param name="packet">Incoming Packet to process</param>
        /// <param name="simulator">Unused</param>
        private void GetAgentNameHandler(Packet packet, Simulator simulator)
        {
            Dictionary<LLUUID, string> names = new Dictionary<LLUUID, string>();
            UUIDNameReplyPacket reply = (UUIDNameReplyPacket)packet;

            #region AvatarsMutex
            AvatarsMutex.WaitOne();

            foreach (UUIDNameReplyPacket.UUIDNameBlockBlock block in reply.UUIDNameBlock)
            {
                if (!Avatars.ContainsKey(block.ID))
                {
                    Avatars[block.ID] = new Avatar();
                    Avatars[block.ID].ID = block.ID;
                }

                Avatars[block.ID].Name = Helpers.FieldToString(block.FirstName) +
                    " " + Helpers.FieldToString(block.LastName);

                names[block.ID] = Avatars[block.ID].Name;
            }

            AvatarsMutex.ReleaseMutex();
            #endregion AvatarsMutex

            if (OnAgentNames != null)
            {
                OnAgentNames(names);
            }
        }

        private void FriendNotificationHandler(Packet packet, Simulator simulator)
        {
            List<LLUUID> requestids = new List<LLUUID>();

            if (packet.Type == PacketType.OnlineNotification)
            {
                // If the agent is online...
                foreach (OnlineNotificationPacket.AgentBlockBlock block in ((OnlineNotificationPacket)packet).AgentBlock)
                {
                    #region AvatarsMutex
                    AvatarsMutex.WaitOne();

                    if (!Avatars.ContainsKey(block.AgentID))
                    {
                        // Mark this avatar for a name request
                        requestids.Add(block.AgentID);

                        Avatars[block.AgentID] = new Avatar();
                        Avatars[block.AgentID].ID = block.AgentID;
                    }

                    Avatars[block.AgentID].Online = true;

                    AvatarsMutex.ReleaseMutex();
                    #endregion AvatarsMutex

                    if (OnFriendNotification != null)
                    {
                        OnFriendNotification(block.AgentID, true);
                    }
                }
            }
            else if (packet.Type == PacketType.OfflineNotification)
            {
                // If the agent is Offline...
                foreach (OfflineNotificationPacket.AgentBlockBlock block in ((OfflineNotificationPacket)packet).AgentBlock)
                {
                    #region AvatarsMutex
                    AvatarsMutex.WaitOne();

                    if (!Avatars.ContainsKey(block.AgentID))
                    {
                        // Mark this avatar for a name request
                        requestids.Add(block.AgentID);

                        Avatars[block.AgentID] = new Avatar();
                        Avatars[block.AgentID].ID = block.AgentID;
                    }

                    Avatars[block.AgentID].Online = false;

                    AvatarsMutex.ReleaseMutex();
                    #endregion AvatarsMutex

                    if (OnFriendNotification != null)
                    {
                        OnFriendNotification(block.AgentID, true);
                    }
                }
            }

            if (requestids.Count > 0)
            {
                BeginGetAvatarNames(requestids, null);
            }
        }
    }
}
{'fraction_non_alphanumeric': 0.08557046979865772, 'fraction_numerical': 0.0007895775759968417, 'mean_word_length': 1.6367421285454071, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 56, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6254320', 'n_tokens_mistral': 2580, 'n_tokens_neox': 2442, 'n_words': 669}