1. Hi all, sorry for my english.
    After each command /test input, free memory on the client side is reduced. How to manually remove all components by CuiRawImageComponent?
    A test case to test the memory leak.

    Code:
    using System;
    using Oxide.Game.Rust.Cui;
    using UnityEngine;namespace Oxide.Plugins
    {
        [Info("Test", "tester", 0.1)]
        [Description("For test")]    class Test : RustPlugin
        {
            [ChatCommand("test")]
            void test(BasePlayer player, string command, string[] args) {
                var elements = new CuiElementContainer();
                var mainPanel = elements.Add(new CuiPanel
                {
                    CursorEnabled = true,
                    Image = { Color = "0.24 0.25 0.25 0.95" },
                    RectTransform = { AnchorMin = "0.1 0.1", AnchorMax = "0.4 0.4" }
                });            elements.Add(new CuiButton
                {
                    Button =
                    {
                        Close = mainPanel,
                        Color = "1.0 0.07 0.07 0.85"
                    },
                    RectTransform =
                    {
                        AnchorMin = "0.95 0.95",
                        AnchorMax = "0.999 1.0005"
                    },
                    Text =
                    {
                        Text = "X",
                        FontSize = 8,
                        Align = TextAnchor.MiddleCenter
                    }
                }, mainPanel);            for (int i = 0; i < 5; i++) {
                    elements.Add(new CuiElement
                    {
                        Name = CuiHelper.GetGuid(),
                        Parent = mainPanel,
                        Components =
                        {
                            new CuiRawImageComponent
                            {
                                Sprite = "assets/content/textures/generic/fulltransparent.tga",
                                Url = "https://homepages.cae.wisc.edu/~ece533/images/airplane.png"
                                //Color = "1.0 1.0 1.0 1.0"
                            },
                            new CuiRectTransformComponent
                            {
                                AnchorMin = "0.0 0.0",
                                AnchorMax = "1.0 0.9"
                            }
                        }
                    });
                }            CuiHelper.AddUi(player, elements);
            }
        }
    }
     
  2. Wulf

    Wulf Community Admin

    You aren't destroying the existing CUI, you are just adding to it. You'd need to store it in a variable per player, and then destroy it when you call it again.
     
  3. I tried to do it, but it does not work. Memory is not released.

    Code:
    class Test : RustPlugin
        {
            private CuiElementContainer elements = new CuiElementContainer();        [ChatCommand("test")]
            void test(BasePlayer player, string command, string[] args) {
                SendReply(player, elements.Count.ToString());
               
                if (elements.Count > 0) {
                    foreach (var elem in elements) {
                        CuiHelper.DestroyUi(player, elem.Name);
                    }
                    elements.Clear();
                }            var mainPanel = elements.Add(new CuiPanel
                {
                    CursorEnabled = true,
                    Image = { Color = "0.24 0.25 0.25 0.95" },
                    RectTransform = { AnchorMin = "0.1 0.1", AnchorMax = "0.4 0.4" }
                });            elements.Add(new CuiButton
                {
                    Button =
                    {
                        Close = mainPanel,
                        Color = "1.0 0.07 0.07 0.85"
                    },
                    RectTransform =
                    {
                        AnchorMin = "0.95 0.95",
                        AnchorMax = "0.999 1.0005"
                    },
                    Text =
                    {
                        Text = "X",
                        FontSize = 8,
                        Align = TextAnchor.MiddleCenter
                    }
                }, mainPanel);            for (int i = 0; i < 5; i++) {
                    elements.Add(new CuiElement
                    {
                        Name = CuiHelper.GetGuid(),
                        Parent = mainPanel,
                        Components =
                        {
                            new CuiRawImageComponent
                            {
                                Sprite = "assets/content/textures/generic/fulltransparent.tga",
                                Url = "https://homepages.cae.wisc.edu/~ece533/images/airplane.png"
                                //Color = "1.0 1.0 1.0 1.0"
                            },
                            new CuiRectTransformComponent
                            {
                                AnchorMin = "0.0 0.0",
                                AnchorMax = "1.0 0.9"
                            }
                        }
                    });
                }            CuiHelper.AddUi(player, elements);
            }
        }
     
  4. Because you are still adding elements to it and not removing it.

    You have your
    private CuiElementContainer elements = new CuiElementContainer();
    See that as a list.
    Every time someone does the command it will add new objects to that list so the list will grow bigger and bigger.
     
  5. I do not understand. I use Clear () to remove all objects from the elements
    Code:
    class Test : RustPlugin
        {
            private CuiElementContainer elements = new CuiElementContainer();        [ChatCommand("test")]
            void test(BasePlayer player, string command, string[] args) {
                SendReply(player, "Before clear: " + elements.Count.ToString());
                if (elements.Count > 0) {
                    foreach (var elem in elements) {
                        CuiHelper.DestroyUi(player, elem.Name);
                    }
                    elements.Clear();
                }
                SendReply(player, "After clear: " + elements.Count.ToString());            var mainPanel = elements.Add(new CuiPanel
                {
                    CursorEnabled = true,
                    Image = { Color = "0.24 0.25 0.25 0.95" },
                    RectTransform = { AnchorMin = "0.1 0.1", AnchorMax = "0.4 0.4" }
                });            elements.Add(new CuiButton
                {
                    Button =
                    {
                        Close = mainPanel,
                        Color = "1.0 0.07 0.07 0.85"
                    },
                    RectTransform =
                    {
                        AnchorMin = "0.95 0.95",
                        AnchorMax = "0.999 1.0005"
                    },
                    Text =
                    {
                        Text = "X",
                        FontSize = 8,
                        Align = TextAnchor.MiddleCenter
                    }
                }, mainPanel);            for (int i = 0; i < 5; i++) {
                    elements.Add(new CuiElement
                    {
                        Name = CuiHelper.GetGuid(),
                        Parent = mainPanel,
                        Components =
                        {
                            new CuiRawImageComponent
                            {
                                Sprite = "assets/content/textures/generic/fulltransparent.tga",
                                Url = "https://homepages.cae.wisc.edu/~ece533/images/airplane.png"
                                //Color = "1.0 1.0 1.0 1.0"
                            },
                            new CuiRectTransformComponent
                            {
                                AnchorMin = "0.0 0.0",
                                AnchorMax = "1.0 0.9"
                            }
                        }
                    });
                }            CuiHelper.AddUi(player, elements);
            }
        }
     
  6. Ah sorry didn't see that.
     
  7. Wulf

    Wulf Community Admin

    I don't think .Clear() does the same as .Destroy().
     
  8. Also isn't
    Code:
    CuiHelper.AddUi(player, elements);
    also staying in the memory?
     
  9. Thanks for your answers guys.
    I tried to use
    Code:
    elements = null;
    GC.Collect();
    and
    Code:
    elements.RemoveRange(0, elements.Count);
    I have not found a method Destroy() for CuiElementsContainer class or List. In Unity there Destroy() method to GameObject class.
     
  10. Wulf

    Wulf Community Admin

    I was a bit off, it's actually CuiHelper.DestroyUi(player, guiInfo). You can find a working example in my Vanish plugin, or the InfoPanel plugin by Nogrod.
     
  11. I used the code from your Vanish plugin to destroy ui. But the memory is still not released
    Code:
    class Test : RustPlugin
        {
            Dictionary<ulong, string> GuiInfo = new Dictionary<ulong, string>();        [ChatCommand("test")]
            void test(BasePlayer player, string command, string[] args) {
                for (int i = 0; i < 5; i++) {
                    string guiInfo;
                    if (GuiInfo.TryGetValue(player.userID, out guiInfo))
                        CuiHelper.DestroyUi(player, guiInfo);                var elements = new CuiElementContainer();                GuiInfo[player.userID] = CuiHelper.GetGuid();
                    elements.Add(new CuiElement
                    {
                        Name = GuiInfo[player.userID],
                        Components =
                        {
                            new CuiRawImageComponent
                            {
                                Sprite = "assets/content/textures/generic/fulltransparent.tga",
                                Url = "https://homepages.cae.wisc.edu/~ece533/images/airplane.png"
                                //Color = "1.0 1.0 1.0 1.0"
                            },
                            new CuiRectTransformComponent
                            {
                                AnchorMin = "0.45 0.15",
                                AnchorMax = "0.55 0.35"
                            }
                        }
                    });                CuiHelper.AddUi(player, elements);
                }
            }        void Unload()
            {
                foreach (var player in BasePlayer.activePlayerList)
                {
                    // Destroy existing UI
                    string guiInfo;
                    if (GuiInfo.TryGetValue(player.userID, out guiInfo)) CuiHelper.DestroyUi(player, guiInfo);
                }
            }
        }
     
  12. Wulf

    Wulf Community Admin

    If it is being destroyed properly, then as far as I know, it's likely a bug with Rust and its CUI. Paging @Nogrod for another look though.
     
  13. He appears to be on to something, because in my testing with the code he has supplied, and it being properly destroyed, it is still using the memory and not freeing it. Running the console commands gc.collect and gc.unload on the client seem to unload most of it, but it isn't done via DestroyUi. (Or so it seems)
     
  14. Wulf

    Wulf Community Admin

    That's pretty much what is going on. The DestroyUi destroys it, but Rust isn't freeing up the memory until the garbage is collected. This would be something that would need to be fixed in Rust.
     
  15. Yes. @Nogrod already helped me. This code works :
    Code:
        class Test : RustPlugin
        {
            Dictionary<ulong, string> GuiInfo = new Dictionary<ulong, string>();        [ChatCommand("test")]
            void test(BasePlayer player, string command, string[] args) {
                for (int i = 0; i < 5; i++) {
                    string guiInfo;
                    if (GuiInfo.TryGetValue(player.userID, out guiInfo))
                        CuiHelper.DestroyUi(player, guiInfo);                var elements = new CuiElementContainer();                GuiInfo[player.userID] = CuiHelper.GetGuid();
                    elements.Add(new CuiElement
                    {
                        Name = GuiInfo[player.userID],
                        Components =
                        {
                            new CuiRawImageComponent
                            {
                                Sprite = "assets/content/textures/generic/fulltransparent.tga",
                                Url = "https://homepages.cae.wisc.edu/~ece533/images/airplane.png"
                                //Color = "1.0 1.0 1.0 1.0"
                            },
                            new CuiRectTransformComponent
                            {
                                AnchorMin = "0.45 0.15",
                                AnchorMax = "0.55 0.35"
                            }
                        }
                    });                CuiHelper.AddUi(player, elements);
                }
                player.Command("gc.unload");
                player.Command("gc.collect");
            }        void Unload()
            {
                foreach (var player in BasePlayer.activePlayerList)
                {
                    // Destroy existing UI
                    string guiInfo;
                    if (GuiInfo.TryGetValue(player.userID, out guiInfo)) CuiHelper.DestroyUi(player, guiInfo);
                }
            }
        }
     
  16. Well, memory is generally only freed when garbage is collected, so this is perfectly fine and I don't see how this is something that's supposed to be fixed.
    In any application memory will increase until the garbage collector fires, unless you're using RC GCs or RC/STW GCs and not pure STW GCs.
    It'd only be an issue if memory was never freed, in which case you either got a memory leak at the C++ level, a reference cycle for RC GCs, a thread leak or you simply didn't account for some reference dragging a lot of garbage along (anonymous classes in Java can cause this issue for instance).

    The reason why Rust frees some memory before a GC fires is because that's the engine stuff written in C++, and that is either RC GC'd or manually freed.
     
  17. Wulf

    Wulf Community Admin

    Sure, I wasn't saying otherwise. It's not really my area anyways.
     
  18. Wouldnt it be nice of someone made a garbage collector plugin? Does the server also need manual garbage collection?
     
  19. Wulf

    Wulf Community Admin

    I'm not sure that would be the best option, probably lead to more issues than good. It's there as a command, but I don't think it's meant to be ran frequently.
     
  20. No, it wouldn't be nice, it's pretty much pointless and will reduce throughput by a lot.
    Neither does the server need manual garbage collection (the suggestion of which makes it seem like you misunderstood the concept).
    You should never call the collect method yourself unless you're blocking the GC due to profiling.
    If you want to reduce throughput for lower latency you should always configure that directly in the GC, not through some script that runs on top of the GC.