Please update as soon as possible.

Please update as soon as possible. It crashes when new players join. It's been crashing every time since the big update.

I'm running the latest version (Server Info, FastBurst, 0.5.9) and it is NOT crashing my server after the latest Rust update when players join the server.

What errors are you seeing in the server logs?

Check your config files for any http image URL links and change them to https://

RickyBalboa

Check your config files for any http image URL links and change them to https://

Unless your images are coming from somewhere else that isn't https:// (i.e., http://)

But, if the server/plugin can't find the specified URL (even if it is NOT a valid url at all) the plugin just displays default placeholder images, it doesn't crash the server.

And I fully tested and verified this on my server before posting this so it's most likely something else causing their crashes that should be indicated in their server logs.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Newtonsoft.Json;
using Oxide.Core;
using Oxide.Core.Libraries;
using Oxide.Game.Rust.Cui;
using UnityEngine;

namespace Oxide.Plugins
{
    [Info("Server Info", "FastBurst,KaZaK", "0.6.0")]
    [Description("UI customizable server info with multiple tabs")]
    public sealed class ServerInfo : RustPlugin
    {
        private static Settings _settings;
        private static readonly Dictionary<ulong, PlayerInfoState> PlayerActiveTabs = new Dictionary<ulong, PlayerInfoState>();
        private static readonly Permission Permission = Interface.GetMod().GetLibrary<Permission>();

        protected override void LoadDefaultConfig()
        {
            Config.Set("settings", Settings.CreateDefault());
        }

        private void OnServerInitialized()
        {
            LoadConfig();
            var configFileName = Manager.ConfigPath + "/server_info_text.json";

            _settings = null;
            try
            {
                var settingsDict = Config.Get("settings") as Dictionary<string, object>;
                _settings = JsonConvert.DeserializeObject<Settings>(JsonConvert.SerializeObject(settingsDict));
            }
            catch (Exception)
            {
                Puts("ServerInfo: Failed to load config");
                return;
            }

            _settings = _settings ?? Settings.CreateDefault();

            if (!_settings.UpgradedConfig && Config.Exists(configFileName))
            {
                try
                {
                    Puts("ServerInfo: Upgrading settings from server_info_text.json");
                    _settings = Config.ReadObject<Settings>(configFileName);
                    _settings.UpgradedConfig = true;
                    Puts("ServerInfo: Successfully upgraded config");
                }
                catch (Exception)
                {
                    Puts("ServerInfo: Failed to upgrade config. Manual editing is required.");
                    Puts("ServerInfo: Copy your settings by parts to new config in ServerInfo.json");
                }
            }

            foreach (var player in BasePlayer.activePlayerList)
                AddHelpButton(player);

            Config.Set("settings", _settings);
            SaveConfig();
        }

        void Unload()
        {
            foreach (var playerActiveTab in PlayerActiveTabs)
            {
                var player = BasePlayer.activePlayerList.FirstOrDefault(f => f.userID == playerActiveTab.Key);
                if (player == null)
                    continue;

                CuiHelper.DestroyUi(player, playerActiveTab.Value.MainPanelName);
                CuiHelper.DestroyUi(player, playerActiveTab.Value.ChatHelpButtonName);
            }

            PlayerActiveTabs.Clear();
        }

        [ConsoleCommand("changetab")]
        private void ChangeTab(ConsoleSystem.Arg arg)
        {
            if (arg.Connection == null || arg.Connection.player == null || !arg.HasArgs(4) || _settings == null)
                return;

            var player = arg.Connection.player as BasePlayer;
            if (player == null)
                return;

            if (!PlayerActiveTabs.ContainsKey(player.userID.Get()))
                return;

            var previousTabIndex = PlayerActiveTabs[player.userID.Get()].ActiveTabIndex;
            var tabToChangeTo = arg.GetInt(0, 65535);

            if (previousTabIndex == tabToChangeTo)
                return;

            var tabToSelectIndex = arg.GetInt(0);
            var activeButtonName = arg.GetString(1);
            var tabToSelectButtonName = arg.GetString(2);
            var mainPanelName = arg.GetString(3);

            CuiHelper.DestroyUi(player, PlayerActiveTabs[player.userID.Get()].ActiveTabContentPanelName);
            CuiHelper.DestroyUi(player, activeButtonName);
            CuiHelper.DestroyUi(player, tabToSelectButtonName);

            var allowedTabs = _settings.Tabs
                .Where((tab, tabIndex) => string.IsNullOrEmpty(tab.OxideGroup) ||
                    tab.OxideGroup.Split(',')
                        .Any(group => Permission.UserHasGroup(player.userID.ToString(), group)))
                .ToList();
            var tabToSelect = allowedTabs[tabToSelectIndex];
            PlayerActiveTabs[player.userID.Get()].ActiveTabIndex = tabToSelectIndex;
            PlayerActiveTabs[player.userID.Get()].PageIndex = 0;

            var container = new CuiElementContainer();
            var tabContentPanelName = CreateTabContent(tabToSelect, container, mainPanelName);
            var newActiveButtonName = AddActiveButton(tabToSelectIndex, tabToSelect, container, mainPanelName);
            AddNonActiveButton(previousTabIndex, container, _settings.Tabs[previousTabIndex], mainPanelName, newActiveButtonName);

            PlayerActiveTabs[player.userID.Get()].ActiveTabContentPanelName = tabContentPanelName;

            SendUI(player, container);
        }

        [ConsoleCommand("changepage")]
        private void ChangePage(ConsoleSystem.Arg arg)
        {
            if (arg.Connection == null || arg.Connection.player == null || !arg.HasArgs(2) || _settings == null)
                return;

            var player = arg.Connection.player as BasePlayer;
            if (player == null)
                return;

            if (!PlayerActiveTabs.ContainsKey(player.userID.Get()))
                return;

            var playerInfoState = PlayerActiveTabs[player.userID.Get()];
            var currentTab = _settings.Tabs[playerInfoState.ActiveTabIndex];
            var currentPageIndex = playerInfoState.PageIndex;

            var pageToChangeTo = arg.GetInt(0, 65535);
            var currentTabContentPanelName = playerInfoState.ActiveTabContentPanelName;
            var mainPanelName = arg.GetString(1);

            if (pageToChangeTo == currentPageIndex)
                return;

            CuiHelper.DestroyUi(player, currentTabContentPanelName);

            playerInfoState.PageIndex = pageToChangeTo;

            var container = new CuiElementContainer();
            var tabContentPanelName = CreateTabContent(currentTab, container, mainPanelName, pageToChangeTo);
            PlayerActiveTabs[player.userID.Get()].ActiveTabContentPanelName = tabContentPanelName;

            SendUI(player, container);
        }

        [ConsoleCommand("infoclose")]
        private void CloseInfo(ConsoleSystem.Arg arg)
        {
            if (arg.Connection == null || arg.Connection.player == null || !arg.HasArgs() || _settings == null)
                return;

            var player = arg.Connection.player as BasePlayer;
            if (player == null)
                return;

            if (!PlayerActiveTabs.ContainsKey(player.userID.Get()))
                return;

            const string defaultName = "defaultString";
            var mainPanelName = arg.GetString(0, defaultName);

            if (mainPanelName.Equals(defaultName, StringComparison.OrdinalIgnoreCase))
                return;

            PlayerInfoState state;
            PlayerActiveTabs.TryGetValue(player.userID.Get(), out state);
            if (state == null)
                return;

            CuiHelper.DestroyUi(player, mainPanelName);

            state.ActiveTabIndex = _settings.TabToOpenByDefault;
            state.MainPanelName = string.Empty;
            state.PageIndex = 0;
        }

        private void OnPlayerConnected(BasePlayer player)
        {
            if (player == null || _settings == null)
                return;

            if (player.HasPlayerFlag(BasePlayer.PlayerFlags.ReceivingSnapshot))
            {
                timer.Once(2, () => OnPlayerConnected(player));
                return;
            }

            PlayerInfoState state;
            PlayerActiveTabs.TryGetValue(player.userID.Get(), out state);

            if (state == null)
            {
                state = new PlayerInfoState(_settings);
                PlayerActiveTabs.Add(player.userID.Get(), state);
            }

            AddHelpButton(player);

            if (!state.InfoShownOnLogin)
                return;
            if (state.InfoShownOnLoginOnce && checkedPlayers.Contains(player.userID.Get()))
                return;
            ShowInfo(player, string.Empty, new string[0]);
            if (state.InfoShownOnLoginOnce)
                checkedPlayers.Add(player.userID.Get());
        }

        List<ulong> checkedPlayers = new List<ulong>();

        private void OnPlayerDisconnected(BasePlayer player, string reason)
        {
            if (player == null || _settings == null)
                return;

            PlayerInfoState state;
            PlayerActiveTabs.TryGetValue(player.userID.Get(), out state);

            if (state == null)
                return;

            CuiHelper.DestroyUi(player, state.MainPanelName);

            state.ActiveTabIndex = _settings.TabToOpenByDefault;
            state.MainPanelName = string.Empty;
            state.PageIndex = 0;
        }

        private static void AddHelpButton(BasePlayer player)
        {
            if (!_settings.HelpButton.IsEnabled || _settings == null)
                return;

            var container = new CuiElementContainer();
            var helpChatButton = CreateHelpButton();
            var helpButtonName = container.Add(helpChatButton);
            if (!PlayerActiveTabs.ContainsKey(player.userID.Get()))
                PlayerActiveTabs[player.userID.Get()] = new PlayerInfoState(_settings);

            PlayerActiveTabs[player.userID.Get()].ChatHelpButtonName = helpButtonName;
            CuiHelper.AddUi(player, container);
        }

        [ConsoleCommand("info")]
        private void ShowConsoleInfo(ConsoleSystem.Arg arg)
        {
            if (arg == null || arg.Connection == null || arg.Connection.player == null || _settings == null)
                return;

            var player = arg.Connection.player as BasePlayer;
            if (player == null)
                return;
            if (string.IsNullOrEmpty(PlayerActiveTabs[player.userID.Get()].MainPanelName))
                ShowInfo(player, string.Empty, null);
        }

        [ChatCommand("help")]
        private void ShowInfo(BasePlayer player, string command, string[] args)
        {
            if (player == null || _settings == null)
                return;

            if (!PlayerActiveTabs.ContainsKey(player.userID.Get()))
                PlayerActiveTabs.Add(player.userID.Get(), new PlayerInfoState(_settings));

            var container = new CuiElementContainer();
            var mainPanelName = AddMainPanel(container);
            PlayerActiveTabs[player.userID.Get()].MainPanelName = mainPanelName;

            var tabFirstIndex = _settings.TabToOpenByDefault;
            var tabToSelectIndex = tabFirstIndex;
            int tabtoSelectArgumentIndex = 0;
            if (args.Count() == 1)
            {
                if (int.TryParse(args[0], out tabtoSelectArgumentIndex))
                {
                    tabToSelectIndex = tabtoSelectArgumentIndex;
                    PlayerActiveTabs[player.userID.Get()].ActiveTabIndex = tabtoSelectArgumentIndex;
                }
            }

            var allowedTabs = _settings.Tabs
                .Where((tab, tabIndex) => string.IsNullOrEmpty(tab.OxideGroup) ||
                    tab.OxideGroup.Split(',')
                        .Any(group => Permission.UserHasGroup(player.userID.ToString(), group)))
                .ToList();
            if (allowedTabs.Count <= 0)
            {
                SendReply(player, "[GUI Help] You don't have permissions to see info.");
                return;
            }

            var activeAllowedTab = allowedTabs[tabToSelectIndex];
            var tabContentPanelName = CreateTabContent(activeAllowedTab, container, mainPanelName);
            var activeTabButtonName = AddActiveButton(tabToSelectIndex, activeAllowedTab, container, mainPanelName);


            for (int tabIndex = 0; tabIndex < allowedTabs.Count; tabIndex++)
            {
                if (tabIndex == tabToSelectIndex)
                    continue;

                AddNonActiveButton(tabIndex, container, allowedTabs[tabIndex], mainPanelName, activeTabButtonName);
            }
            PlayerActiveTabs[player.userID.Get()].ActiveTabContentPanelName = tabContentPanelName;
            SendUI(player, container);
        }

        private static void SendUI(BasePlayer player, CuiElementContainer container)
        {
            try
            {
                CuiHelper.AddUi(player, container);
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error sending UI to player {player.displayName}: {ex.Message}");
            }
        }

        private static string AddMainPanel(CuiElementContainer container)
        {
            Color backgroundColor;
            ColorExtensions.TryParseHexString(_settings.BackgroundColor, out backgroundColor);
            var mainPanel = new CuiPanel
            {
                Image =
                {
                    Color = ColorExtensions.ToRustFormatString(backgroundColor)
                },
                CursorEnabled = true,
                RectTransform =
                {
                    AnchorMin = _settings.Position.GetRectTransformAnchorMin(),
                    AnchorMax = _settings.Position.GetRectTransformAnchorMax()
                }
            };

            var tabContentPanelName = container.Add(mainPanel);
            if (!_settings.BackgroundImage.Enabled)
                return tabContentPanelName;

            var backgroundImage = CreateImage(tabContentPanelName, _settings.BackgroundImage);

            container.Add(backgroundImage);

            return tabContentPanelName;
        }

        private static CuiElement CreateImage(string panelName, ImageSettings settings)
        {
            var element = new CuiElement();
            
            string url = settings.Url;
            if (url.StartsWith("http://"))
            {
                url = url.Replace("http://", "https://");
            }
            
            var image = new CuiRawImageComponent
            {
                Url = url, 
                Color = string.Format("1 1 1 {0:F1}", (settings.TransparencyInPercent / 100.0f)),
                Sprite = "assets/content/textures/generic/fulltransparent.tga"
            };
            
            var position = settings.Position;
            var rectTransform = new CuiRectTransformComponent
            {
                AnchorMin = position.GetRectTransformAnchorMin(),
                AnchorMax = position.GetRectTransformAnchorMax()
            };
            
            element.Components.Add(image);
            element.Components.Add(rectTransform);
            element.Name = CuiHelper.GetGuid();
            element.Parent = panelName;

            return element;
        }

        private string CreateTabContent(HelpTab helpTab, CuiElementContainer container, string mainPanelName, int pageIndex = 0)
        {
            var tabPanelName = CreateTab(helpTab, container, mainPanelName, pageIndex);
            var closeButton = CreateCloseButton(mainPanelName, _settings.CloseButtonColor);
            container.Add(closeButton, tabPanelName);
            return tabPanelName;
        }

        private static string CreateTab(HelpTab helpTab, CuiElementContainer container, string mainPanelName, int pageIndex)
        {
            var tabPanelName = CreateTabPanel(container, mainPanelName, "#00000000");

            var currentPage = helpTab.Pages.ElementAtOrDefault(pageIndex);
            if (currentPage == null)
                return tabPanelName;

            foreach (var imageSettings in currentPage.ImageSettings)
            {
                var imageObject = CreateImage(tabPanelName, imageSettings);
                container.Add(imageObject);
            }

            var cuiLabel = CreateHeaderLabel(helpTab);
            container.Add(cuiLabel, tabPanelName);

            const float firstLineMargin = 0.91f;
            const float textLineHeight = 0.04f;

            for (var textRow = 0; textRow < currentPage.TextLines.Count; textRow++)
            {
                var textLine = currentPage.TextLines[textRow];
                var textLineLabel = CreateTextLineLabel(helpTab, firstLineMargin, textLineHeight, textRow, textLine);
                container.Add(textLineLabel, tabPanelName);
            }

            if (pageIndex > 0)
            {
                var prevPageButton = CreatePrevPageButton(mainPanelName, pageIndex, _settings.PrevPageButtonColor);
                container.Add(prevPageButton, tabPanelName);
            }

            if (helpTab.Pages.Count - 1 == pageIndex)
                return tabPanelName;

            var nextPageButton = CreateNextPageButton(mainPanelName, pageIndex, _settings.NextPageButtonColor);
            container.Add(nextPageButton, tabPanelName);

            return tabPanelName;
        }

        private static string CreateTabPanel(CuiElementContainer container, string mainPanelName, string hexColor)
        {
            Color backgroundColor;
            ColorExtensions.TryParseHexString(hexColor, out backgroundColor);

            return container.Add(new CuiPanel
            {
                CursorEnabled = false,
                Image =
                {
                    Color = ColorExtensions.ToRustFormatString(backgroundColor),
                },
                RectTransform =
                {
                    AnchorMin = "0.22 0.01",
                    AnchorMax = "0.99 0.98"
                }
            }, mainPanelName);
        }

        private static string CreateTabContentPanel(CuiElementContainer container, string mainPanelName, string hexColor)
        {
            Color backgroundColor;
            ColorExtensions.TryParseHexString(hexColor, out backgroundColor);

            return container.Add(new CuiPanel
            {
                CursorEnabled = false,
                Image =
                {
                    Color = ColorExtensions.ToRustFormatString(backgroundColor),
                },
                RectTransform =
                {
                    AnchorMin = "0 0",
                    AnchorMax = "0.86 1"
                }
            }, mainPanelName);
        }

        private static CuiLabel CreateHeaderLabel(HelpTab helpTab)
        {
            return new CuiLabel
            {
                RectTransform =
                {
                    AnchorMin = "0.01 0.85",
                    AnchorMax = "1.0 0.98"
                },
                Text =
                {
                    Align = helpTab.HeaderAnchor,
                    FontSize = helpTab.HeaderFontSize,
                    Text = helpTab.HeaderText
                }
            };
        }

        private static CuiButton CreateCloseButton(string mainPanelName, string hexColor)
        {
            Color color;
            ColorExtensions.TryParseHexString(hexColor, out color);
            return new CuiButton
            {
                Button =
                {
                    Command = string.Format("infoclose {0}", mainPanelName),
                    Close = mainPanelName,
                    Color = ColorExtensions.ToRustFormatString(color)
                },
                RectTransform =
                {
                    AnchorMin = "0.86 0.93",
                    AnchorMax = "0.97 0.99"
                },
                Text =
                {
                    Text = _settings.CloseButtonText,
                    FontSize = 18,
                    Align = TextAnchor.MiddleCenter
                }
            };
        }

        private static CuiButton CreateHelpButton()
        {
            Color color;
            ColorExtensions.TryParseHexString(_settings.HelpButton.Color, out color);
            return new CuiButton
            {
                Button =
                {
                    Command = "info",
                    Color = ColorExtensions.ToRustFormatString(color)
                },
                RectTransform =
                {
                    AnchorMin = _settings.HelpButton.Position.GetRectTransformAnchorMin(),
                    AnchorMax = _settings.HelpButton.Position.GetRectTransformAnchorMax()
                },
                Text =
                {
                    Text = _settings.HelpButton.Text,
                    FontSize = _settings.HelpButton.FontSize,
                    Align = TextAnchor.MiddleCenter
                }
            };
        }

        private static CuiLabel CreateTextLineLabel(HelpTab helpTab, float firstLineMargin, float textLineHeight, int textRow,
            string textLine)
        {
            var textLineLabel = new CuiLabel
            {
                RectTransform =
                {
                    AnchorMin = "0.01 " + (firstLineMargin - textLineHeight * (textRow + 1)),
                    AnchorMax = "0.85 " + (firstLineMargin - textLineHeight * textRow)
                },
                Text =
                {
                    Align = helpTab.TextAnchor,
                    FontSize = helpTab.TextFontSize,
                    Text = textLine
                }
            };
            return textLineLabel;
        }

        private static CuiButton CreatePrevPageButton(string mainPanelName, int pageIndex, string hexColor)
        {
            Color color;
            ColorExtensions.TryParseHexString(hexColor, out color);
            return new CuiButton
            {
                Button =
                {
                    Command = string.Format("changepage {0} {1}", pageIndex - 1, mainPanelName),
                    Color = ColorExtensions.ToRustFormatString(color)
                },
                RectTransform =
                {
                    AnchorMin = "0.86 0.01",
                    AnchorMax = "0.97 0.07"
                },
                Text =
                {
                    Text = _settings.PrevPageText,
                    FontSize = 18,
                    Align = TextAnchor.MiddleCenter
                }
            };
        }

        private static CuiButton CreateNextPageButton(string mainPanelName, int pageIndex, string hexColor)
        {
            Color color;
            ColorExtensions.TryParseHexString(hexColor, out color);
            return new CuiButton
            {
                Button =
                {
                    Command = string.Format("changepage {0} {1}", pageIndex + 1, mainPanelName),
                    Color = ColorExtensions.ToRustFormatString(color)
                },
                RectTransform =
                {
                    AnchorMin = "0.86 0.08",
                    AnchorMax = "0.97 0.15"
                },
                Text =
                {
                    Text = _settings.NextPageText,
                    FontSize = 18,
                    Align = TextAnchor.MiddleCenter
                }
            };
        }

        private static void AddNonActiveButton(
             int tabIndex,
             CuiElementContainer container,
             HelpTab helpTab,
             string mainPanelName,
             string activeTabButtonName)
        {
            Color nonActiveButtonColor;
            ColorExtensions.TryParseHexString(_settings.InactiveButtonColor, out nonActiveButtonColor);

            CuiButton helpTabButton = CreateTabButton(tabIndex, helpTab, nonActiveButtonColor);
            string helpTabButtonName = container.Add(helpTabButton, mainPanelName);
            string command = string.Format("changeTab {0} {1} {2} {3}", tabIndex, activeTabButtonName, helpTabButtonName, mainPanelName);
            helpTabButton.Button.Command = command;
        }

        private static string AddActiveButton(
            int activeTabIndex,
            HelpTab activeTab,
            CuiElementContainer container,
            string mainPanelName)
        {
            Color activeButtonColor;
            ColorExtensions.TryParseHexString(_settings.ActiveButtonColor, out activeButtonColor);

            var activeHelpTabButton = CreateTabButton(activeTabIndex, activeTab, activeButtonColor);
            var activeTabButtonName = container.Add(activeHelpTabButton, mainPanelName);
            var command = string.Format("changeTab {0}", activeTabIndex);

            activeHelpTabButton.Button.Command = command;
            return activeTabButtonName;
        }

        private static CuiButton CreateTabButton(int tabIndex, HelpTab helpTab, Color color)
        {
            const float verticalMargin = 0.03f;
            const float buttonHeight = 0.06f;

            return new CuiButton
            {
                Button =
                {
                    Color = ColorExtensions.ToRustFormatString(color)
                },
                RectTransform =
                {
                    AnchorMin = string.Format("0.01 {0}", 1 - ((verticalMargin + buttonHeight) * (tabIndex + 1))),
                    AnchorMax = string.Format("0.20 {0}", 1 - ((verticalMargin * (tabIndex + 1)) + (tabIndex * buttonHeight)))
                },
                Text =
                {
                    Text = helpTab.ButtonText,
                    FontSize = helpTab.TabButtonFontSize,
                    Align = helpTab.TabButtonAnchor
                }
            };
        }

        [JsonObject]
        public sealed class Settings
        {
            public Settings()
            {
                Tabs = new List<HelpTab>();
                ShowInfoOnPlayerInit = true;
                ShowInfoOnlyOncePerRuntime = true;
                TabToOpenByDefault = 0;
                Position = new Position();

                ActiveButtonColor = "#" + ColorExtensions.ToHexStringRGBA(Color.cyan);
                InactiveButtonColor = "#" + ColorExtensions.ToHexStringRGBA(Color.gray);
                CloseButtonColor = "#" + ColorExtensions.ToHexStringRGBA(Color.gray);
                PrevPageButtonColor = "#" + ColorExtensions.ToHexStringRGBA(Color.gray);
                NextPageButtonColor = "#" + ColorExtensions.ToHexStringRGBA(Color.gray);
                BackgroundColor = "#" + ColorExtensions.ToHexStringRGBA(new Color(0f, 0f, 0f, 1.0f));
                NextPageText = "Next Page";
                PrevPageText = "Prev Page";
                CloseButtonText = "Close";

                HelpButton = new HelpButtonSettings();

                BackgroundImage = new BackgroundImageSettings();
            }

            public List<HelpTab> Tabs { get; set; }
            public bool ShowInfoOnPlayerInit { get; set; }
            public bool ShowInfoOnlyOncePerRuntime { get; set; }

            public int TabToOpenByDefault { get; set; }

            public Position Position { get; set; }
            public BackgroundImageSettings BackgroundImage { get; set; }

            public string ActiveButtonColor { get; set; }
            public string InactiveButtonColor { get; set; }
            public string CloseButtonColor { get; set; }
            public string CloseButtonText { get; set; }
            public string NextPageButtonColor { get; set; }
            public string NextPageText { get; set; }
            public string PrevPageButtonColor { get; set; }
            public string PrevPageText { get; set; }
            public string BackgroundColor { get; set; }

            public HelpButtonSettings HelpButton { get; set; }

            public bool UpgradedConfig { get; set; }

            public static Settings CreateDefault()
            {
                var settings = new Settings();
                var firstTab = new HelpTab
                {
                    ButtonText = "First Tab",
                    HeaderText = "First Tab",
                    Pages =
                    {
                        new HelpTabPage
                        {
                            TextLines =
                            {
                                "This is first tab, first page.",
                                "Add some text here by adding more lines.",
                                "You should replace all default text lines with whatever you feel up to",
                                "type <color=red> /info </color> to open this window",
                                "Press next page to check second page.",
                                "You may add more pages in config file."
                            },
                            ImageSettings =
                            {
                                new ImageSettings
                                {
                                    Position = new Position
                                    {
                                        MinX = 0,
                                        MaxX = 0.5f,
                                        MinY = 0,
                                        MaxY = 0.5f
                                    },
                                    Url = "https://i.imgur.com/placeholder.png",
                                    TransparencyInPercent = 100
                                },
                                new ImageSettings
                                {
                                    Position = new Position
                                    {
                                        MinX = 0.5f,
                                        MaxX = 1f,
                                        MinY = 0,
                                        MaxY = 0.5f
                                    },
                                    Url = "https://i.imgur.com/placeholder.png",
                                    TransparencyInPercent = 100
                                },
                                new ImageSettings
                                {
                                    Position = new Position
                                    {
                                        MinX = 0,
                                        MaxX = 0.5f,
                                        MinY = 0.5f,
                                        MaxY = 1f
                                    },
                                    Url = "https://i.imgur.com/placeholder.png",
                                    TransparencyInPercent = 100
                                },
                                new ImageSettings
                                {
                                    Position = new Position
                                    {
                                        MinX = 0.5f,
                                        MaxX = 1f,
                                        MinY = 0.5f,
                                        MaxY = 1f
                                    },
                                    Url = "https://i.imgur.com/placeholder.png",
                                    TransparencyInPercent = 100
                                },
                            }
                        },
                        new HelpTabPage
                        {
                            TextLines =
                            {
                                "This is first tab, second page",
                                "Add some text here by adding more lines.",
                                "You should replace all default text lines with whatever you feel up to",
                                "type <color=red> /info </color> to open this window",
                                "Press next page to check third page.",
                                "Press prev page to go back to first page.",
                                "You may add more pages in config file."
                            }
                        },
                        new HelpTabPage
                        {
                            TextLines =
                            {
                                "This is first tab, third page",
                                "Add some text here by adding more lines.",
                                "You should replace all default text lines with whatever you feel up to",
                                "type <color=red> /info </color> to open this window",
                                "Press prev page to go back to second page.",
                            }
                        }
                    }
                };
                var secondTab = new HelpTab
                {
                    ButtonText = "Second Tab",
                    HeaderText = "Second Tab",
                    Pages =
                    {
                        new HelpTabPage
                        {
                            TextLines =
                            {
                                "This is second tab, first page.",
                                "Add some text here by adding more lines.",
                                "You should replace all default text lines with whatever you feel up to",
                                "type <color=red> /info </color> to open this window",
                                "You may add more pages in config file."
                            }
                        }
                    }
                };
                var thirdTab = new HelpTab
                {
                    ButtonText = "Third Tab",
                    HeaderText = "Third Tab",
                    Pages =
                    {
                        new HelpTabPage
                        {
                            TextLines =
                            {
                                "This is third tab, first page.",
                                "Add some text here by adding more lines.",
                                "You should replace all default text lines with whatever you feel up to",
                                "type <color=red> /info </color> to open this window",
                                "You may add more pages in config file."
                            }
                        }
                    }
                };

                settings.Tabs.Add(firstTab);
                settings.Tabs.Add(secondTab);
                settings.Tabs.Add(thirdTab);

                return settings;
            }
        }

        public sealed class Position
        {
            public Position()
            {
                MinX = 0.15f;
                MaxX = 0.9f;
                MinY = 0.2f;
                MaxY = 0.9f;
            }

            public float MinX { get; set; }
            public float MaxX { get; set; }
            public float MinY { get; set; }
            public float MaxY { get; set; }

            public string GetRectTransformAnchorMin()
            {
                return string.Format("{0} {1}", MinX, MinY);
            }

            public string GetRectTransformAnchorMax()
            {
                return string.Format("{0} {1}", MaxX, MaxY);
            }
        }

        public sealed class HelpTab
        {
            private string _headerText;
            private string _buttonText;

            public HelpTab()
            {
                ButtonText = "Default ServerInfo Help Tab";
                HeaderText = "Default ServerInfo Help";
                Pages = new List<HelpTabPage>();
                TextFontSize = 16;
                HeaderFontSize = 32;
                TabButtonFontSize = 16;
                TextAnchor = TextAnchor.MiddleLeft;
                HeaderAnchor = TextAnchor.UpperLeft;
                TabButtonAnchor = TextAnchor.MiddleCenter;
                OxideGroup = string.Empty;
            }

            public string ButtonText
            {
                get { return string.IsNullOrEmpty(_buttonText) ? _headerText : _buttonText; }
                set { _buttonText = value; }
            }

            public string HeaderText
            {
                get
                {
                    return string.IsNullOrEmpty(_headerText) ? _buttonText : _headerText;
                }
                set { _headerText = value; }
            }

            public List<HelpTabPage> Pages { get; set; }

            public TextAnchor TabButtonAnchor { get; set; }
            public int TabButtonFontSize { get; set; }

            public TextAnchor HeaderAnchor { get; set; }
            public int HeaderFontSize { get; set; }

            public int TextFontSize { get; set; }
            public TextAnchor TextAnchor { get; set; }

            public string OxideGroup { get; set; }
        }

        public sealed class HelpTabPage
        {
            public List<string> TextLines { get; set; }
            public List<ImageSettings> ImageSettings { get; set; }

            public HelpTabPage()
            {
                TextLines = new List<string>();
                ImageSettings = new List<ImageSettings>();
            }
        }

        public class ImageSettings
        {
            public Position Position { get; set; }
            public string Url { get; set; }
            public int TransparencyInPercent { get; set; }

            public ImageSettings()
            {
                Position = new Position
                {
                    MaxX = 1.0f,
                    MaxY = 1.0f,
                    MinY = 0.0f,
                    MinX = 0.0f
                };
                Url = "https://i.imgur.com/placeholder.png";
                TransparencyInPercent = 100;
            }
        }

        public sealed class BackgroundImageSettings : ImageSettings
        {
            public bool Enabled { get; set; }

            public BackgroundImageSettings()
            {
                Enabled = false;
            }
        }

        public sealed class HelpButtonSettings
        {
            public bool IsEnabled { get; set; }
            public string Text { get; set; }
            public Position Position { get; set; }
            public string Color { get; set; }
            public int FontSize { get; set; }

            public HelpButtonSettings()
            {
                IsEnabled = false;
                Text = "Help";
                Color = "#" + ColorExtensions.ToHexStringRGBA(UnityEngine.Color.gray);

                FontSize = 18;

                Position = new Position { MinX = 0.00f, MaxX = 0.05f, MinY = 0.10f, MaxY = 0.14f };
            }
        }

        public sealed class PlayerInfoState
        {
            public PlayerInfoState(Settings settings)
            {
                if (settings == null) throw new ArgumentNullException("settings");

                ActiveTabIndex = settings.TabToOpenByDefault;
                PageIndex = 0;
                InfoShownOnLogin = settings.ShowInfoOnPlayerInit;
                InfoShownOnLoginOnce = settings.ShowInfoOnlyOncePerRuntime;
                ActiveTabContentPanelName = string.Empty;
                ChatHelpButtonName = string.Empty;
                MainPanelName = string.Empty;
            }

            public int ActiveTabIndex { get; set; }
            public int PageIndex { get; set; }
            public bool InfoShownOnLogin { get; set; }
            public bool InfoShownOnLoginOnce { get; set; }
            public string ActiveTabContentPanelName { get; set; }
            public string ChatHelpButtonName { get; set; }
            public string MainPanelName { get; set; }
        }

        public static class ColorExtensions
        {
            public static string ToRustFormatString(Color color)
            {
                float r = UnityEngine.Mathf.Clamp01(color.r);
                float g = UnityEngine.Mathf.Clamp01(color.g);
                float b = UnityEngine.Mathf.Clamp01(color.b);
                float a = UnityEngine.Mathf.Clamp01(color.a);
                
                return string.Format("{0:F2} {1:F2} {2:F2} {3:F2}", r, g, b, a);
            }

            public static string ToHexStringRGB(Color col)
            {
                Color32 color = col;
                return string.Format("{0:X2}{1:X2}{2:X2}", color.r, color.g, color.b);
            }

            public static string ToHexStringRGBA(Color col)
            {
                Color32 color = col;
                return string.Format("{0:X2}{1:X2}{2:X2}{3:X2}", color.r, color.g, color.b, color.a);
            }

            public static bool TryParseHexString(string hexString, out Color color)
            {
                try
                {
                    color = FromHexString(hexString);
                    return true;
                }
                catch
                {
                    color = Color.white;
                    return false;
                }
            }

            private static Color FromHexString(string hexString)
            {
                if (string.IsNullOrEmpty(hexString))
                {
                    throw new InvalidOperationException("Cannot convert an empty/null string.");
                }
                var trimChars = new[] { '#' };
                var str = hexString.Trim(trimChars);
                switch (str.Length)
                {
                    case 3:
                        {
                            var chArray2 = new[] { str[0], str[0], str[1], str[1], str[2], str[2], 'F', 'F' };
                            str = new string(chArray2);
                            break;
                        }
                    case 4:
                        {
                            var chArray3 = new[] { str[0], str[0], str[1], str[1], str[2], str[2], str[3], str[3] };
                            str = new string(chArray3);
                            break;
                        }
                    default:
                        if (str.Length < 6)
                        {
                            str = str.PadRight(6, '0');
                        }
                        if (str.Length < 8)
                        {
                            str = str.PadRight(8, 'F');
                        }
                        break;
                }
                var r = byte.Parse(str.Substring(0, 2), NumberStyles.HexNumber);
                var g = byte.Parse(str.Substring(2, 2), NumberStyles.HexNumber);
                var b = byte.Parse(str.Substring(4, 2), NumberStyles.HexNumber);
                var a = byte.Parse(str.Substring(6, 2), NumberStyles.HexNumber);

                return new Color32(r, g, b, a);
            }
        }
    }
}
UPDATE!!! I don't want to infringe on copyright, but here's my update. It's now only https:

@Vnuk ... You do realise it was NOT necessary to change any CS code for this (asked rhetirically)?

Because any server owner can simply edit their settings file and set any properly formed known good URLs for their images. 

In other words, this did NOT require a CODE CHANGE, it required the server admin to update the URLs in their settings file.

That being said, it's unfortunate that miokawach didn't post any error logs and/or provide other indications as to why the server was crashing.  Without that information we are all just taking pot-shots into the dark on this.

Again, the original unaltered verion of this plugin is running just fine on my server and many other servers.  Nobody is crashing my server when they log-in (at least not on my server).  And the plugin runs flawlessly when the image URL is HTTP, HTTPS, or some totally concocket garbage ... in all cases the plugin simply loads a default image vs. crashing any server.

So given all of the above, there has to be some other issue on that server causing this, and Server Info was just an assumption (false positive).

harold95

as previously noted the config file for this plugin has 2 references to "http" and needs to be changed to "https".

Curious as to how this could cause the server to crash when players log into the server as indicated in the initial post.

Especially after I tested this every way I could think of on my server ... http, https, good URLs, bad URls, good/bad image files, and even a total gobblygook mixmatch of random text ... and in every case the plugin ran flawlessly and nobody crashed my server when they logged in.

Not saying admins don't need to use known good properly formed URLs to known good images, just questioning how BAD URLs/IMAGES could crash a server when players log in when that obviously is NOT the case on my or other servers (based on the lack of others posting with the same issue).  And Vnuk rewriting the code to replace http with https was celarly not a solution to any server crash issue.

Just saying that there is obviously something else going on here that is crashing the server when players log in, and this http vs. https issue though valid is just a read hearing.

And with 32+ years of experience in various IT & software developemt fields I know what I'm talking about too. ;)

Just saying that there is obviously something else going on here that is crashing the server when players log in, and this http vs. https issue though valid is just a read hearing.

It's not! Facepunch decided to disable http in the bundeld unity, throwing an exception. Many plugins were affected at the beginning of the month.The server itself was also affected if, for example, the map or logo was loaded via http. But some plugins automatically rewrite http urls to use https, so if you test them, you won't notice.

qJItzycZHrB8Igq.jpg ToliburtiX

It's not! Facepunch decided to disable http in the bundeld unity, throwing an exception. Many plugins were affected at the beginning of the month.The server itself was also affected if, for example, the map or logo was loaded via http. But some plugins automatically rewrite http urls to use https, so if you test them, you won't notice.

Then please explain to me how exactly my server is running without crashing when players log into my server with anything and everything, both good or bad URLs, http or https, in my ServerInfo settings file when running the original unchanges version of the ServerInfo code?

If as you say this issue was caused by changes to the base game code, then my server should be crashing the same as everyone is reporting.

All I'm saying is that more detailed info is required as I'm obviously NOT experienging this issue given the same setup as everyone else.

Especially for you I turned back an url to http in my serverinfo config. When opening the menu I get the red error message (screenshot), and the image is not loaded. A short google search brings up this page: "Insecure connection is not allowed" While it's not exactly a description of an error in rust, it clearly shows that there is on option in the unity package manager to allow/disallow download over http. Don't know, why you have no problems, be happy about it.  ;-)

http error
qJItzycZHrB8Igq.jpg ToliburtiX
Especially for you I turned back an url to http in my serverinfo config. When opening the menu I get the red error message (screenshot), and the image is not loaded. A short google search brings up this page: "Insecure connection is not allowed" While it's not exactly a description of an error in rust, it clearly shows that there is on option in the unity package manager to allow/disallow download over http. Don't know, why you have no problems, be happy about it.  ;-)

That is what my testing reveiled as well, error messages but NO SERVER CRASHES!

So given you post, the only question I have is if that CRASHED your server?  And as it displayed an error on your game screen (I've seen those many times before) I can only assume there wasn't an actual server CRASH thereafter. 

And that being the case, logged/displayed ERROR notifications are one thing, outright SERVER CRASHES are something completely different. ;)

Thanks for posting! 

f9Xs0PrxghD0jKd.png DiGiaComTech

That is what my testing reveiled as well, error messages but NO SERVER CRASHES!

So given you post, the only question I have is if that CRASHED your server?  And as it displayed an error on your game screen (I've seen those many times before) I can only assume there wasn't an actual server CRASH thereafter. 

And that being the case, logged/displayed ERROR notifications are one thing, outright SERVER CRASHES are something completely different. ;)

Thanks for posting! 

As far as I can see, the thread opener says:

"Please update as soon as possible. It crashes when new players join. It's been crashing every time since the big update."

Which clearly means the plugin crashes, no word about the server. Well technically speaking it throws an uncaught exception, and there is no need to reload it.

You were the first and only one who is constantly talking about "server crash" in this thread. Well, clearly not in connection with this plugin, but the origin of the problem, the server crashes or does not run properly (name it like you wish),  if you have a custom map loaded with "levelurl" option. http does not work anymore. 

qJItzycZHrB8Igq.jpg ToliburtiX

As far as I can see, the thread opener says:

"Please update as soon as possible. It crashes when new players join. It's been crashing every time since the big update."

Which clearly means the plugin crashes, no word about the server. Well technically speaking it throws an uncaught exception, and there is no need to reload it.

You were the first and only one who is constantly talking about "server crash" in this thread. Well, clearly not in connection with this plugin, but the origin of the problem, the server crashes or does not run properly (name it like you wish),  if you have a custom map loaded with "levelurl" option. http does not work anymore. 

First off, based on the initial report, one can only ASSUME 'IT CRASHES' was referring to the plugin vs. the server.  And as we all well know the term Crash normally reffers to the Server going down whereas plugins simply stop working and/or fail to load and/or provide some sort of notification on screen and/or in the server  logs.

And that is exaclty why I asked for clarification, for logs, for some additional information instead of making ASSUMPTIONS.

Second and more important, even if it was only the plugin that was failing, then why did I NOT receive the same error notifications when I tested this on my server with the old/original version of this plugin with every possible mis-formed URL I could think of (posted above)?  And I think I know now know why (keep reading).

And if this was all caused by HTTP vs. HTTPS being in the settings file, then why would anyone need to update the plugin code when all anyone needed to do was update their settings file?  I'm all for 'idiot proffing' plugins as much as posible, but in these cases, server admin have to take initial responsibility for things like this.

And as you just now posted (first I'm hearing of it), if this involved a custom map loaded with "levelurl" option, then wouldn't that indicate an issue related to that plugin (or whatever it is) vs. the Server Info plugin (asked rhetorically)?

Bottom line for me is, I simply asked for additional information from the original poster (they declined to reply) and I added my comments on posts from others.  All of which is expressly what Help Forums are for aren't they (asked rhetorically)?

Nuff said ... [EOL]