using Kodnix.Character; namespace KianaBH.Util; public class IConsole { public static readonly string PrefixContent = "[KianaBH]> "; public static readonly string Prefix = $"\u001b[38;2;255;192;203m{PrefixContent}\u001b[0m"; private static readonly int HistoryMaxCount = 10; public static List Input { get; set; } = []; private static int CursorIndex { get; set; } = 0; private static readonly List InputHistory = []; private static int HistoryIndex = -1; public static event Action? OnConsoleExcuteCommand; public static void InitConsole() { Console.Title = ConfigManager.Config.GameServer.GameServerName; } public static int GetWidth(string str) => str.ToCharArray().Sum(EastAsianWidth.GetLength); public static void RedrawInput(List input, bool hasPrefix = true) => RedrawInput(new string([.. input]), hasPrefix); public static void RedrawInput(string input, bool hasPrefix = true) { var length = GetWidth(input); if (hasPrefix) { input = Prefix + input; length += GetWidth(PrefixContent); } if (Console.GetCursorPosition().Left > 0) Console.SetCursorPosition(0, Console.CursorTop); Console.Write(input + new string(' ', Console.BufferWidth - length)); Console.SetCursorPosition(length, Console.CursorTop); } #region Handlers public static void HandleEnter() { var input = new string([.. Input]); if (string.IsNullOrWhiteSpace(input)) return; // New line Console.WriteLine(); Input = []; CursorIndex = 0; if (InputHistory.Count >= HistoryMaxCount) InputHistory.RemoveAt(0); InputHistory.Add(input); HistoryIndex = InputHistory.Count; // Handle command if (input.StartsWith('/')) input = input[1..].Trim(); OnConsoleExcuteCommand?.Invoke(input); } public static void HandleBackspace() { if (CursorIndex <= 0) return; CursorIndex--; var targetWidth = GetWidth(Input[CursorIndex].ToString()); Input.RemoveAt(CursorIndex); var (left, _) = Console.GetCursorPosition(); Console.SetCursorPosition(left - targetWidth, Console.CursorTop); var remain = new string([.. Input.Skip(CursorIndex)]); Console.Write(remain + new string(' ', targetWidth)); Console.SetCursorPosition(left - targetWidth, Console.CursorTop); } public static void HandleUpArrow() { if (InputHistory.Count == 0) return; if (HistoryIndex > 0) { HistoryIndex--; var history = InputHistory[HistoryIndex]; Input = [.. history]; CursorIndex = Input.Count; RedrawInput(Input); } } public static void HandleDownArrow() { if (HistoryIndex >= InputHistory.Count) return; HistoryIndex++; if (HistoryIndex >= InputHistory.Count) { HistoryIndex = InputHistory.Count; Input = []; CursorIndex = 0; } else { var history = InputHistory[HistoryIndex]; Input = [.. history]; CursorIndex = Input.Count; } RedrawInput(Input); } public static void HandleLeftArrow() { if (CursorIndex <= 0) return; var (left, _) = Console.GetCursorPosition(); CursorIndex--; Console.SetCursorPosition(left - GetWidth(Input[CursorIndex].ToString()), Console.CursorTop); } public static void HandleRightArrow() { if (CursorIndex >= Input.Count) return; var (left, _) = Console.GetCursorPosition(); CursorIndex++; Console.SetCursorPosition(left + GetWidth(Input[CursorIndex - 1].ToString()), Console.CursorTop); } public static void HandleInput(ConsoleKeyInfo keyInfo) { if (char.IsControl(keyInfo.KeyChar)) return; if (Input.Count >= (Console.BufferWidth - PrefixContent.Length)) return; HandleInput(keyInfo.KeyChar); } public static void HandleInput(char keyChar) { Input.Insert(CursorIndex, keyChar); CursorIndex++; var (left, _) = Console.GetCursorPosition(); Console.Write(new string([.. Input.Skip(CursorIndex - 1)])); Console.SetCursorPosition(left + GetWidth(keyChar.ToString()), Console.CursorTop); } #endregion public static string ListenConsole() { while (true) { ConsoleKeyInfo keyInfo; try { keyInfo = Console.ReadKey(true); } catch (InvalidOperationException) { continue; } switch (keyInfo.Key) { case ConsoleKey.Enter: HandleEnter(); break; case ConsoleKey.Backspace: HandleBackspace(); break; case ConsoleKey.LeftArrow: HandleLeftArrow(); break; case ConsoleKey.RightArrow: HandleRightArrow(); break; case ConsoleKey.UpArrow: HandleUpArrow(); break; case ConsoleKey.DownArrow: HandleDownArrow(); break; default: HandleInput(keyInfo); break; } } } }