예제 #1
0
        /// <summary>
        /// This function *blocks* the calling thread, and caches all the images required to display the message, then registers them with the provided font.
        /// </summary>
        /// <param name="p_Message">The chat message to get images from</param>
        /// <param name="p_Font">The font to register these images to</param>
        public static bool PrepareImages(IChatMessage p_Message, Extensions.EnhancedFontInfo p_Font)
        {
            var l_Tasks = new List <Task <BeatSaberPlus.Utils.EnhancedImageInfo> >();
            var l_PendingEmoteDownloads = new HashSet <string>();

            foreach (var l_Emote in p_Message.Emotes)
            {
                if (l_PendingEmoteDownloads.Contains(l_Emote.Id))
                {
                    continue;
                }

                if (!p_Font.CharacterLookupTable.ContainsKey(l_Emote.Id))
                {
                    l_PendingEmoteDownloads.Add(l_Emote.Id);

                    TaskCompletionSource <BeatSaberPlus.Utils.EnhancedImageInfo> l_TaskCompletionSource = new TaskCompletionSource <BeatSaberPlus.Utils.EnhancedImageInfo>();

                    switch (l_Emote.Type)
                    {
                    case EmoteType.SingleImage:
                        SharedCoroutineStarter.instance.StartCoroutine(BeatSaberPlus.Utils.ChatImageProvider.instance.TryCacheSingleImage(l_Emote.Id, l_Emote.Uri, l_Emote.IsAnimated, (l_Info) => {
                            if (l_Info != null && !p_Font.TryRegisterImageInfo(l_Info, out var l_Character))
                            {
                                Logger.Instance.Warn($"Failed to register emote \"{l_Emote.Id}\" in font {p_Font.Font.name}.");
                            }

                            l_TaskCompletionSource.SetResult(l_Info);
                        }, p_ForcedHeight: 110));
                        break;

                    case EmoteType.SpriteSheet:
                        BeatSaberPlus.Utils.ChatImageProvider.instance.TryCacheSpriteSheetImage(l_Emote.Id, l_Emote.Uri, l_Emote.UVs, (l_Info) =>
                        {
                            if (l_Info != null && !p_Font.TryRegisterImageInfo(l_Info, out var l_Character))
                            {
                                Logger.Instance.Warn($"Failed to register emote \"{l_Emote.Id}\" in font {p_Font.Font.name}.");
                            }

                            l_TaskCompletionSource.SetResult(l_Info);
                        }, p_ForcedHeight: 110);
                        break;

                    default:
                        l_TaskCompletionSource.SetResult(null);
                        break;
                    }

                    l_Tasks.Add(l_TaskCompletionSource.Task);
                }
            }

            foreach (var l_Badge in p_Message.Sender.Badges)
            {
                if (l_PendingEmoteDownloads.Contains(l_Badge.Id))
                {
                    continue;
                }

                if (!p_Font.CharacterLookupTable.ContainsKey(l_Badge.Id))
                {
                    l_PendingEmoteDownloads.Add(l_Badge.Id);
                    TaskCompletionSource <BeatSaberPlus.Utils.EnhancedImageInfo> l_TaskCompletionSource = new TaskCompletionSource <BeatSaberPlus.Utils.EnhancedImageInfo>();
                    SharedCoroutineStarter.instance.StartCoroutine(BeatSaberPlus.Utils.ChatImageProvider.instance.TryCacheSingleImage(l_Badge.Id, l_Badge.Uri, false, (p_Info) => {
                        if (p_Info != null && !p_Font.TryRegisterImageInfo(p_Info, out var l_Character))
                        {
                            Logger.Instance.Warn($"Failed to register badge \"{l_Badge.Id}\" in font {p_Font.Font.name}.");
                        }

                        l_TaskCompletionSource.SetResult(p_Info);
                    }, p_ForcedHeight: 100));
                    l_Tasks.Add(l_TaskCompletionSource.Task);
                }
            }

            // Wait on all the resources to be ready
            return(Task.WaitAll(l_Tasks.ToArray(), 15000));
        }
예제 #2
0
        /// <summary>
        /// Build a message
        /// </summary>
        /// <param name="p_Message">Message informations</param>
        /// <param name="p_Font">Font</param>
        /// <returns></returns>
#pragma warning disable CS1998
        public static async Task <string> BuildMessage(IChatMessage p_Message, Extensions.EnhancedFontInfo p_Font)
#pragma warning restore CS1998
        {
            try
            {
                if (!PrepareImages(p_Message, p_Font))
                {
                    Logger.Instance.Warn($"Failed to prepare some/all images for msg \"{p_Message.Message}\"!");
                }

                ConcurrentStack <BeatSaberPlus.Utils.EnhancedImageInfo> l_Badges = new ConcurrentStack <BeatSaberPlus.Utils.EnhancedImageInfo>();
                foreach (var l_Badge in p_Message.Sender.Badges)
                {
                    if (!BeatSaberPlus.Utils.ChatImageProvider.instance.CachedImageInfo.TryGetValue(l_Badge.Id, out var l_BadgeInfo))
                    {
                        Logger.Instance.Warn($"Failed to find cached image info for badge \"{l_Badge.Id}\"!");
                        continue;
                    }

                    l_Badges.Push(l_BadgeInfo);
                }

                /// Replace all instances of < with a zero-width non-breaking character
                StringBuilder l_StringBuilder = new StringBuilder(p_Message.Message.Replace("<", "<\u2060"));

                foreach (var l_Emote in p_Message.Emotes)
                {
                    if (!BeatSaberPlus.Utils.ChatImageProvider.instance.CachedImageInfo.TryGetValue(l_Emote.Id, out var replace))
                    {
                        Logger.Instance.Warn($"Emote {l_Emote.Name} was missing from the emote dict! The request to {l_Emote.Uri} may have timed out?");
                        continue;
                    }

                    if (!p_Font.TryGetCharacter(replace.ImageID, out uint p_Character))
                    {
                        Logger.Instance.Warn($"Emote {l_Emote.Name} was missing from the character dict! Font hay have run out of usable characters.");
                        continue;
                    }

                    try
                    {
                        l_StringBuilder.Replace(l_Emote.Name,
                                                ((l_Emote is TwitchEmote) && ((TwitchEmote)l_Emote).Bits > 0)
                                ?
                                                $"{char.ConvertFromUtf32((int)p_Character)}\u00A0<color={((TwitchEmote)l_Emote).Color.PadRight(9, 'F').ToLower()}><size=77%><b>{((TwitchEmote)l_Emote).Bits}\u00A0</b></size></color>"
                                :
                                                char.ConvertFromUtf32((int)p_Character)
                                                , l_Emote.StartIndex, l_Emote.EndIndex - l_Emote.StartIndex + 1);
                    }
                    catch (Exception p_Exception)
                    {
                        Logger.Instance.Error($"An unknown error occurred while trying to swap emote {l_Emote.Name} into string of length {l_StringBuilder.Length} at location ({l_Emote.StartIndex}, {l_Emote.EndIndex})");
                        Logger.Instance.Error(p_Exception);
                    }
                }

                /// System messages get a grayish color to differentiate them from normal messages in chat, and do not receive a username/badge prefix
                if (p_Message.IsSystemMessage)
                {
                    l_StringBuilder.Insert(0, $"<color=#bbbbbbbb>");
                    l_StringBuilder.Append("</color>");
                }
                else
                {
                    /// Message becomes the color of their name if it's an action message
                    if (p_Message.IsActionMessage)
                    {
                        l_StringBuilder.Insert(0, $"<color={p_Message.Sender.Color}><b>{p_Message.Sender.DisplayName}</b> ");
                        l_StringBuilder.Append("</color>");
                    }
                    /// Insert username w/ color
                    else
                    {
                        l_StringBuilder.Insert(0, $"<color={p_Message.Sender.Color}><b>{p_Message.Sender.DisplayName}</b></color>: ");
                    }

                    for (int l_I = 0; l_I < p_Message.Sender.Badges.Length; l_I++)
                    {
                        /// Insert user badges at the beginning of the string in reverse order
                        if (l_Badges.TryPop(out var l_Badge) && p_Font.TryGetCharacter(l_Badge.ImageID, out var l_Character))
                        {
                            l_StringBuilder.Insert(0, $"{char.ConvertFromUtf32((int)l_Character)} ");
                        }
                    }
                }

                return(l_StringBuilder.ToString());
            }
            catch (Exception p_Exception)
            {
                Logger.Instance.Error($"An exception occurred in ChatMessageBuilder while parsing msg with {p_Message.Emotes.Length} emotes. Msg: \"{p_Message.Message}\"");
                Logger.Instance.Error(p_Exception);
            }

            return(p_Message.Message);
        }