From 512445cb1d6489cd7297ef8f43ca6a0cd407e071 Mon Sep 17 00:00:00 2001 From: BruceChen Date: Fri, 3 Apr 2026 03:01:15 +0800 Subject: [PATCH] fix: prevent TUI StackOverflowException from excessive log controls The TUI log view used an ItemsControl with 5000 max entries and no UI virtualization. Avalonia's composition renderer traverses the entire visual tree on each frame -- with thousands of TextBlock controls, the recursive Render/RenderCore calls exceed the thread stack size on constrained devices (especially ARM where each stack frame is larger due to ABI differences), causing a StackOverflowException in ServerCompositionContainerVisual.Render. Changes: - Enable VirtualizingStackPanel on the log ItemsControl so Avalonia only creates visuals for the rows currently in the viewport. - Add [Console.General] TUI_Log_Scrollback config option so users can control max log lines in TUI mode. Default is 0 (automatic: 3000 on x86/x64, 500 on ARM/ARM64). Made-with: Cursor --- .../ConfigComments/ConfigComments.Designer.cs | 18 ++++++++++++++++++ .../ConfigComments/ConfigComments.resx | 6 ++++++ MinecraftClient/Settings.cs | 3 +++ MinecraftClient/Tui/MainTuiView.cs | 15 ++++++++++++++- 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs index d94cab66..36687b26 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.Designer.cs @@ -1429,6 +1429,24 @@ namespace MinecraftClient { } } + /// + /// Looks up a localized string similar to Maximum number of input history records to keep.. + /// + internal static string Console_General_History_Input_Records { + get { + return ResourceManager.GetString("Console.General.History_Input_Records", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Maximum log lines kept in TUI mode scrollback. Set to 0 for automatic (3000 on x86/x64, 500 on ARM).. + /// + internal static string Console_General_TUI_Log_Scrollback { + get { + return ResourceManager.GetString("Console.General.TUI_Log_Scrollback", resourceCulture); + } + } + /// /// Looks up a localized string similar to Startup Config File ///Please do not record extraneous data in this file as it will be overwritten by MCC. diff --git a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx index 825c7e76..b1f7e220 100644 --- a/MinecraftClient/Resources/ConfigComments/ConfigComments.resx +++ b/MinecraftClient/Resources/ConfigComments/ConfigComments.resx @@ -587,6 +587,12 @@ Custom colors are only available when using "vt100_24bit" color mode. You can use "Ctrl+P" to print out the current input and cursor position. + + Maximum number of input history records to keep. + + + Maximum log lines kept in TUI mode scrollback. Set to 0 for automatic. + Startup Config File Please do not record extraneous data in this file as it will be overwritten by MCC. diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index ededd636..ce1524fc 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -1218,6 +1218,9 @@ namespace MinecraftClient [TomlInlineComment("$Console.General.History_Input_Records$")] public int History_Input_Records = 32; + + [TomlInlineComment("$Console.General.TUI_Log_Scrollback$")] + public int TUI_Log_Scrollback = 0; } [TomlDoNotInlineObject] diff --git a/MinecraftClient/Tui/MainTuiView.cs b/MinecraftClient/Tui/MainTuiView.cs index d8155d48..9affb94c 100644 --- a/MinecraftClient/Tui/MainTuiView.cs +++ b/MinecraftClient/Tui/MainTuiView.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; +using System.Runtime.InteropServices; using Avalonia; using Avalonia.Controls; using Avalonia.Controls.Primitives; @@ -16,9 +17,20 @@ namespace MinecraftClient.Tui { public class MainTuiView : UserControl { - private const int MaxLogLines = 5000; + private static readonly int MaxLogLines = ResolveMaxLogLines(); private const int CtrlCDoublePressMsec = 1500; + private static int ResolveMaxLogLines() + { + int configured = Settings.Config.Console.General.TUI_Log_Scrollback; + if (configured > 0) + return configured; + + bool isArm = RuntimeInformation.ProcessArchitecture + is Architecture.Arm or Architecture.Arm64; + return isArm ? 500 : 3000; + } + private readonly ObservableCollection _logLines = new(); private readonly ObservableCollection _logControls = new(); private readonly ItemsControl _logItemsControl; @@ -78,6 +90,7 @@ namespace MinecraftClient.Tui { ItemsSource = _logControls, Focusable = false, + ItemsPanel = new FuncTemplate(() => new VirtualizingStackPanel()), }; _logScrollViewer = new ScrollViewer