private void OnServerFound(ServerDiscovery.Server server, bool isNew)
        {
            if (!isNew)
            {
                return;
            }

            serverList.Add(server);
        }
        public override void OnPaint(Canvas canvas, Rect view)
        {
            IDrawDevice device = canvas.DrawDevice;

            Vector2 center = device.TargetSize * 0.5f;

            const float topLine    = 96f;
            float       bottomLine = device.TargetSize.Y - 42;

            api.DrawMaterial("MenuDim", center.X, (topLine + bottomLine) * 0.5f, Alignment.Center, ColorRgba.White, 55f, (bottomLine - topLine) * 0.063f, new Rect(0f, 0.3f, 1f, 0.4f));

            int charOffset = 0;
            int itemCount  = 0;

            if (serverList.Count > 0)
            {
                const float itemSpacing = 17f;

                float topItem       = topLine - 4f;
                float bottomItem    = bottomLine - 10f;
                float contentHeight = bottomItem - topItem;

                float maxVisibleItemsFloat = (contentHeight / itemSpacing);
                maxVisibleItems = (int)maxVisibleItemsFloat;

                float currentItem = topItem + itemSpacing + (maxVisibleItemsFloat - maxVisibleItems) * 0.5f * itemSpacing;

                float column2 = device.TargetSize.X * 0.55f;
                float sx      = column2 * 1.52f;
                float column1 = column2;
                float column3 = column2;
                column1 *= 0.3f;
                column2 *= 0.78f;
                column3 *= 1.08f;

                for (int i = 0; i < maxVisibleItems; i++)
                {
                    int idx = i + scrollOffset;
                    if (idx >= serverList.Count)
                    {
                        break;
                    }

                    ServerDiscovery.Server server = serverList[idx];
                    if (server.IsLost && server.LatencyMs < 0)
                    {
                        continue;
                    }

                    string    infoText = server.CurrentPlayers + " / " + server.MaxPlayers + "   ";
                    ColorRgba infoColor;
                    if (server.LatencyMs < 0)
                    {
                        infoText += "- ms";
                        infoColor = new ColorRgba(0.48f, 0.5f);
                    }
                    else if (server.LatencyMs > 10000)
                    {
                        infoText  = "menu/play custom/multi/unreachable".T();
                        infoColor = new ColorRgba(0.45f, 0.27f, 0.22f, 0.5f);
                    }
                    else
                    {
                        infoText += server.LatencyMs + " ms";

                        float playersRatio = (float)(server.CurrentPlayers / server.MaxPlayers);
                        if (server.LatencyMs < 50 && playersRatio < 0.9f)
                        {
                            infoColor = new ColorRgba(0.2f, 0.45f, 0.2f, 0.5f);
                        }
                        else if (server.LatencyMs < 100 && playersRatio < 0.9f)
                        {
                            infoColor = new ColorRgba(0.45f, 0.45f, 0.21f, 0.5f);
                        }
                        else if (server.LatencyMs < 200 && playersRatio < 0.95f)
                        {
                            infoColor = new ColorRgba(0.5f, 0.4f, 0.2f, 0.5f);
                        }
                        else if (server.LatencyMs < 400 && playersRatio < 0.99f)
                        {
                            infoColor = new ColorRgba(0.47f, 0.35f, 0.3f, 0.5f);
                        }
                        else
                        {
                            infoColor = new ColorRgba(0.45f, 0.27f, 0.22f, 0.5f);
                        }
                    }

                    string name = server.Name;
                    if (name.Length > 32)
                    {
                        name = name.Substring(0, 31) + "...";
                    }

                    string endpoint = server.ActiveEndPointName;
                    if (endpoint.Length > 26)
                    {
                        endpoint = endpoint.Substring(0, 25) + "...";
                    }

                    if (selectedIndex == idx)
                    {
                        charOffset = 0;

                        float xMultiplier = name.Length * 0.5f;
                        float easing      = Ease.OutElastic(animation);
                        float x           = column1 + xMultiplier - easing * xMultiplier;
                        float size        = 0.7f + easing * 0.1f;

                        // Column 2
                        api.DrawStringShadow(ref charOffset, infoText, column2, currentItem, Alignment.Left,
                                             infoColor, 0.8f, 0.4f, 1f, 1f, 8f, charSpacing: 0.88f);

                        // Column 3
                        api.DrawStringShadow(ref charOffset, endpoint, column3, currentItem, Alignment.Left,
                                             new ColorRgba(0.48f, 0.5f), 0.8f, 0.4f, 1f, 1f, 8f, charSpacing: 0.88f);

                        // Column 1
                        api.DrawStringShadow(ref charOffset, name, x, currentItem, Alignment.Left,
                                             null, size, 0.4f, 1f, 1f, 8f, charSpacing: 0.88f);
                    }
                    else
                    {
                        // Column 2
                        api.DrawString(ref charOffset, infoText, column2, currentItem, Alignment.Left,
                                       infoColor, 0.7f);

                        // Column 3
                        api.DrawString(ref charOffset, endpoint, column3, currentItem, Alignment.Left,
                                       ColorRgba.TransparentBlack, 0.7f);

                        // Column 1
                        api.DrawString(ref charOffset, name, column1, currentItem, Alignment.Left,
                                       ColorRgba.TransparentBlack, 0.7f);
                    }

                    currentItem += itemSpacing;
                    itemCount++;
                }

                // Scrollbar
                if (itemCount > maxVisibleItems)
                {
                    const float sw = 3f;
                    float       sy = ((float)scrollOffset / itemCount) * 18f * maxVisibleItems + topLine;
                    float       sh = ((float)maxVisibleItems / itemCount) * 16f * maxVisibleItems;

                    BatchInfo mat1 = device.RentMaterial();
                    mat1.Technique = DrawTechnique.Alpha;
                    mat1.MainColor = new ColorRgba(0f, 0f, 0f, 0.28f);
                    canvas.State.SetMaterial(mat1);
                    canvas.FillRect(sx + 1f, sy + 1f, sw, sh);

                    BatchInfo mat2 = device.RentMaterial();
                    mat2.Technique = DrawTechnique.Alpha;
                    mat2.MainColor = new ColorRgba(0.8f, 0.8f, 0.8f, 0.5f);
                    canvas.State.SetMaterial(mat2);
                    canvas.FillRect(sx, sy, sw, sh);
                }
            }

            if (itemCount == 0)
            {
                api.DrawStringShadow(ref charOffset, "menu/play custom/multi/empty".T(), center.X, center.Y, Alignment.Center,
                                     new ColorRgba(0.62f, 0.44f, 0.34f, 0.5f), 0.9f, 0.4f, 0.6f, 0.6f, 8f, charSpacing: 0.88f);
            }

            api.DrawMaterial("MenuLine", 0, center.X, topLine, Alignment.Center, ColorRgba.White, 1.6f);
            api.DrawMaterial("MenuLine", 1, center.X, bottomLine, Alignment.Center, ColorRgba.White, 1.6f);
        }