From 38a890f840f63789f576939e6057eb9e6305d707 Mon Sep 17 00:00:00 2001 From: ReinforceZwei <39955851+ReinforceZwei@users.noreply.github.com> Date: Fri, 29 Jan 2021 07:45:18 +0800 Subject: [PATCH] Implement new logger (#1426) * Implement multi-channel logger * Implement chat filter * Improve a bit * Improvement * Add debug message filter and filter mode * Avoid duplicate debug prefix string Co-authored-by: ORelio --- MinecraftClient/ChatBot.cs | 5 +- MinecraftClient/ILogger.cs | 36 +++++ MinecraftClient/MCLogger.cs | 140 ++++++++++++++++++ MinecraftClient/McClient.cs | 68 +++++---- MinecraftClient/MinecraftClient.csproj | 2 + .../Protocol/Handlers/Protocol18.cs | 19 +-- .../Protocol/IMinecraftComHandler.cs | 1 + .../Resources/config/MinecraftClient.ini | 12 +- MinecraftClient/Settings.cs | 37 ++++- 9 files changed, 277 insertions(+), 43 deletions(-) create mode 100644 MinecraftClient/ILogger.cs create mode 100644 MinecraftClient/MCLogger.cs diff --git a/MinecraftClient/ChatBot.cs b/MinecraftClient/ChatBot.cs index 7b333d70..7024c6c4 100644 --- a/MinecraftClient/ChatBot.cs +++ b/MinecraftClient/ChatBot.cs @@ -704,7 +704,10 @@ namespace MinecraftClient /// Log text to write protected void LogToConsole(object text) { - ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", this.GetType().Name, text)); + if (_handler == null || master == null) + ConsoleIO.WriteLogLine(String.Format("[{0}] {1}", this.GetType().Name, text)); + else + Handler.Log.Info(String.Format("[{0}] {1}", this.GetType().Name, text)); string logfile = Settings.ExpandVars(Settings.chatbotLogFile); if (!String.IsNullOrEmpty(logfile)) diff --git a/MinecraftClient/ILogger.cs b/MinecraftClient/ILogger.cs new file mode 100644 index 00000000..283bfc7c --- /dev/null +++ b/MinecraftClient/ILogger.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient +{ + public interface ILogger + { + bool DebugEnabled { get; set; } + bool WarnEnabled { get; set; } + bool InfoEnabled { get; set; } + bool ErrorEnabled { get; set; } + bool ChatEnabled { get; set; } + + void Info(string msg); + void Info(string msg, params object[] args); + void Info(object msg); + + void Debug(string msg); + void Debug(string msg, params object[] args); + void Debug(object msg); + + void Warn(string msg); + void Warn(string msg, params object[] args); + void Warn(object msg); + + void Error(string msg); + void Error(string msg, params object[] args); + void Error(object msg); + + void Chat(string msg); + void Chat(string msg, params object[] args); + void Chat(object msg); + } +} diff --git a/MinecraftClient/MCLogger.cs b/MinecraftClient/MCLogger.cs new file mode 100644 index 00000000..6f42d08e --- /dev/null +++ b/MinecraftClient/MCLogger.cs @@ -0,0 +1,140 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace MinecraftClient +{ + public class MCLogger : ILogger + { + private bool debugEnabled = false; + private bool warnEnabled = true; + private bool infoEnabled = true; + private bool errorEnabled = true; + private bool chatEnabled = true; + public bool DebugEnabled { get { return debugEnabled; } set { debugEnabled = value; } } + public bool WarnEnabled { get { return warnEnabled; } set { warnEnabled = value; } } + public bool InfoEnabled { get { return infoEnabled; } set { infoEnabled = value; } } + public bool ErrorEnabled { get { return errorEnabled; } set { errorEnabled = value; } } + public bool ChatEnabled { get { return chatEnabled; } set { chatEnabled = value; } } + + public void Debug(string msg) + { + if (debugEnabled) + { + if (Settings.DebugFilter != null) + { + var shouldLog = Settings.DebugFilter.IsMatch(msg); // assumed whitelist mode + if (Settings.FilterMode == Settings.FilterModeEnum.Blacklist) + shouldLog = !shouldLog; // blacklist mode so flip result + if (!shouldLog) + return; + // Don't write debug lines here as it could cause a stack overflow + } + Log("§8[DEBUG] " + msg); + } + } + + public void Debug(string msg, params object[] args) + { + Debug(string.Format(msg, args)); + } + + public void Debug(object msg) + { + Debug(msg.ToString()); + } + + public void Info(object msg) + { + if (infoEnabled) + ConsoleIO.WriteLogLine(msg.ToString()); + } + + public void Info(string msg) + { + if (infoEnabled) + ConsoleIO.WriteLogLine(msg); + } + + public void Info(string msg, params object[] args) + { + if (infoEnabled) + ConsoleIO.WriteLogLine(string.Format(msg, args)); + } + + public void Warn(string msg) + { + if (warnEnabled) + Log("§6[WARN] " + msg); + } + + public void Warn(string msg, params object[] args) + { + Warn(string.Format(msg, args)); + } + + public void Warn(object msg) + { + Warn(msg.ToString()); + } + + public void Error(string msg) + { + if (errorEnabled) + Log("§c[ERROR] " + msg); + } + + public void Error(string msg, params object[] args) + { + Error(string.Format(msg, args)); + } + + public void Error(object msg) + { + Error(msg.ToString()); + } + + public void Chat(string msg) + { + if (chatEnabled) + { + if (Settings.ChatFilter != null) + { + var shouldLog = Settings.ChatFilter.IsMatch(msg); // assumed whitelist mode + if (Settings.FilterMode == Settings.FilterModeEnum.Blacklist) + shouldLog = !shouldLog; // blacklist mode so flip result + if (shouldLog) + Log(msg); + else Debug("[Logger] One Chat message filtered: " + msg); + } + else Log(msg); + } + } + + public void Chat(string msg, params object[] args) + { + Chat(string.Format(msg, args)); + } + + public void Chat(object msg) + { + Chat(msg.ToString()); + } + + private void Log(object msg) + { + ConsoleIO.WriteLineFormatted(msg.ToString()); + } + + private void Log(string msg) + { + ConsoleIO.WriteLineFormatted(msg); + } + + private void Log(string msg, params object[] args) + { + ConsoleIO.WriteLineFormatted(string.Format(msg, args)); + } + } +} diff --git a/MinecraftClient/McClient.cs b/MinecraftClient/McClient.cs index 18ea656b..d4e9e20d 100644 --- a/MinecraftClient/McClient.cs +++ b/MinecraftClient/McClient.cs @@ -107,6 +107,7 @@ namespace MinecraftClient public int GetGamemode() { return gamemode; } public bool GetNetworkPacketCaptureEnabled() { return networkPacketCaptureEnabled; } public int GetProtocolVersion() { return protocolversion; } + public ILogger GetLogger() { return this.Log; } // get bots list for unloading them by commands public List GetLoadedChatBots() @@ -119,6 +120,8 @@ namespace MinecraftClient Thread cmdprompt; Thread timeoutdetector; + public ILogger Log; + /// /// Starts the main chat client /// @@ -173,6 +176,13 @@ namespace MinecraftClient this.port = port; this.protocolversion = protocolversion; + this.Log = new MCLogger(); + Log.DebugEnabled = Settings.DebugMessages; + Log.InfoEnabled = Settings.InfoMessages; + Log.ChatEnabled = Settings.ChatMessages; + Log.WarnEnabled = Settings.WarningMessages; + Log.ErrorEnabled = Settings.ErrorMessages; + if (!singlecommand) { /* Load commands from Commands namespace */ @@ -208,7 +218,7 @@ namespace MinecraftClient client.ReceiveBufferSize = 1024 * 1024; client.ReceiveTimeout = 30000; // 30 seconds handler = Protocol.ProtocolHandler.GetProtocolHandler(client, protocolversion, forgeInfo, this); - Translations.WriteLine("mcc.version_supported"); + Log.Info(Translations.Get("mcc.version_supported")); try { @@ -217,7 +227,7 @@ namespace MinecraftClient if (singlecommand) { handler.SendChatMessage(command); - ConsoleIO.WriteLineFormatted(Translations.Get("mcc.single_cmd", command)); + Log.Info(Translations.Get("mcc.single_cmd", command)); Thread.Sleep(5000); handler.Disconnect(); Thread.Sleep(1000); @@ -228,7 +238,7 @@ namespace MinecraftClient BotLoad(bot, false); botsOnHold.Clear(); - Translations.WriteLine("mcc.joined", (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar)); + Log.Info(Translations.Get("mcc.joined", (Settings.internalCmdChar == ' ' ? "" : "" + Settings.internalCmdChar))); cmdprompt = new Thread(new ThreadStart(CommandPrompt)); cmdprompt.Name = "MCC Command prompt"; @@ -241,21 +251,21 @@ namespace MinecraftClient } else { - Translations.WriteLine("error.login_failed"); + Log.Error(Translations.Get("error.login_failed")); retry = true; } } catch (Exception e) { - ConsoleIO.WriteLineFormatted("§8" + e.GetType().Name + ": " + e.Message); - Translations.WriteLine("error.join"); + Log.Error(e.GetType().Name + ": " + e.Message); + Log.Error(Translations.Get("error.join")); retry = true; } } catch (SocketException e) { - ConsoleIO.WriteLineFormatted("§8" + e.Message); - Translations.WriteLine("error.connect"); + Log.Error(e.Message); + Log.Error(Translations.Get("error.connect")); retry = true; } @@ -263,7 +273,7 @@ namespace MinecraftClient { if (ReconnectionAttemptsLeft > 0) { - ConsoleIO.WriteLogLine(Translations.Get("mcc.reconnect", ReconnectionAttemptsLeft)); + Log.Info(Translations.Get("mcc.reconnect", ReconnectionAttemptsLeft)); Thread.Sleep(5000); ReconnectionAttemptsLeft--; Program.Restart(); @@ -316,7 +326,7 @@ namespace MinecraftClient } else if (response_msg.Length > 0) { - ConsoleIO.WriteLogLine(response_msg); + Log.Info(response_msg); } } else SendText(text); @@ -394,7 +404,7 @@ namespace MinecraftClient { if (!(e is ThreadAbortException)) { - ConsoleIO.WriteLogLine(Translations.Get("icmd.error", bot.ToString(), e.ToString())); + Log.Warn(Translations.Get("icmd.error", bot.ToString(), e.ToString())); } else throw; //ThreadAbortException should not be caught } @@ -430,7 +440,7 @@ namespace MinecraftClient } catch (Exception e) { - ConsoleIO.WriteLogLine(e.Message); + Log.Warn(e.Message); } } } @@ -489,17 +499,17 @@ namespace MinecraftClient { case ChatBot.DisconnectReason.ConnectionLost: message = Translations.Get("mcc.disconnect.lost"); - ConsoleIO.WriteLine(message); + Log.Info(message); break; case ChatBot.DisconnectReason.InGameKick: - Translations.WriteLine("mcc.disconnect.server"); - ConsoleIO.WriteLineFormatted(message); + Log.Info(Translations.Get("mcc.disconnect.server")); + Log.Info(message); break; case ChatBot.DisconnectReason.LoginRejected: - ConsoleIO.WriteLine("mcc.disconnect.login"); - ConsoleIO.WriteLineFormatted(message); + Log.Info(Translations.Get("mcc.disconnect.login")); + Log.Info(message); break; case ChatBot.DisconnectReason.UserLogout: @@ -516,7 +526,7 @@ namespace MinecraftClient { if (!(e is ThreadAbortException)) { - ConsoleIO.WriteLogLine("OnDisconnect: Got error from " + bot.ToString() + ": " + e.ToString()); + Log.Warn("OnDisconnect: Got error from " + bot.ToString() + ": " + e.ToString()); } else throw; //ThreadAbortException should not be caught } @@ -541,7 +551,7 @@ namespace MinecraftClient { if (!(e is ThreadAbortException)) { - ConsoleIO.WriteLogLine("Update: Got error from " + bot.ToString() + ": " + e.ToString()); + Log.Warn("Update: Got error from " + bot.ToString() + ": " + e.ToString()); } else throw; //ThreadAbortException should not be caught } @@ -1515,7 +1525,7 @@ namespace MinecraftClient string parentMethodName = method.Name; //Display a meaningful error message to help debugging the ChatBot - ConsoleIO.WriteLogLine(parentMethodName + ": Got error from " + bot.ToString() + ": " + e.ToString()); + Log.Error(parentMethodName + ": Got error from " + bot.ToString() + ": " + e.ToString()); } else throw; //ThreadAbortException should not be caught here as in can happen when disconnecting from server } @@ -1560,7 +1570,7 @@ namespace MinecraftClient { inventoryHandlingRequested = false; inventoryHandlingEnabled = true; - Translations.WriteLogLine("extra.inventory_enabled"); + Log.Info(Translations.Get("extra.inventory_enabled")); } ClearInventories(); @@ -1577,7 +1587,7 @@ namespace MinecraftClient { terrainAndMovementsEnabled = true; terrainAndMovementsRequested = false; - Translations.WriteLogLine("extra.terrainandmovement_enabled"); + Log.Info(Translations.Get("extra.terrainandmovement_enabled")); } if (terrainAndMovementsEnabled) @@ -1702,11 +1712,11 @@ namespace MinecraftClient text = ChatParser.ParseText(json, links); } - ConsoleIO.WriteLineFormatted(text, true); + Log.Chat(text); if (Settings.DisplayChatLinks) foreach (string link in links) - ConsoleIO.WriteLogLine(Translations.Get("mcc.link", link), false); + Log.Chat(Translations.Get("mcc.link", link), false); DispatchBotEvent(bot => bot.GetText(text)); DispatchBotEvent(bot => bot.GetText(text, json)); @@ -1734,8 +1744,8 @@ namespace MinecraftClient if (inventoryID != 0) { - ConsoleIO.WriteLogLine(Translations.Get("extra.inventory_open", inventoryID, inventory.Title)); - Translations.WriteLogLine("extra.inventory_interact"); + Log.Info(Translations.Get("extra.inventory_open", inventoryID, inventory.Title)); + Log.Info(Translations.Get("extra.inventory_interact")); DispatchBotEvent(bot => bot.OnInventoryOpen(inventoryID)); } } @@ -1751,7 +1761,7 @@ namespace MinecraftClient if (inventoryID != 0) { - ConsoleIO.WriteLogLine(Translations.Get("extra.inventory_close", inventoryID)); + Log.Info(Translations.Get("extra.inventory_close", inventoryID)); DispatchBotEvent(bot => bot.OnInventoryClose(inventoryID)); } } @@ -2084,12 +2094,12 @@ namespace MinecraftClient { if (Settings.AutoRespawn) { - Translations.WriteLogLine("mcc.player_dead_respawn"); + Log.Info(Translations.Get("mcc.player_dead_respawn")); respawnTicks = 10; } else { - Translations.WriteLogLine("mcc.player_dead"); + Log.Info(Translations.Get("mcc.player_dead")); } DispatchBotEvent(bot => bot.OnDeath()); } diff --git a/MinecraftClient/MinecraftClient.csproj b/MinecraftClient/MinecraftClient.csproj index b2095270..b2599159 100644 --- a/MinecraftClient/MinecraftClient.csproj +++ b/MinecraftClient/MinecraftClient.csproj @@ -119,6 +119,7 @@ True DefaultConfigResource.resx + @@ -157,6 +158,7 @@ + diff --git a/MinecraftClient/Protocol/Handlers/Protocol18.cs b/MinecraftClient/Protocol/Handlers/Protocol18.cs index ec9af78e..6c7b43c0 100644 --- a/MinecraftClient/Protocol/Handlers/Protocol18.cs +++ b/MinecraftClient/Protocol/Handlers/Protocol18.cs @@ -67,6 +67,7 @@ namespace MinecraftClient.Protocol.Handlers SocketWrapper socketWrapper; DataTypes dataTypes; Thread netRead; + ILogger log; public Protocol18Handler(TcpClient Client, int protocolVersion, IMinecraftComHandler handler, ForgeInfo forgeInfo) { @@ -79,22 +80,23 @@ namespace MinecraftClient.Protocol.Handlers this.pForge = new Protocol18Forge(forgeInfo, protocolVersion, dataTypes, this, handler); this.pTerrain = new Protocol18Terrain(protocolVersion, dataTypes, handler); this.packetPalette = new PacketTypeHandler(protocolVersion, forgeInfo != null).GetTypeHandler(); + this.log = handler.GetLogger(); if (handler.GetTerrainEnabled() && protocolversion > MC1165Version) { - Translations.WriteLineFormatted("extra.terrainandmovement_disabled"); + log.Error(Translations.Get("extra.terrainandmovement_disabled")); handler.SetTerrainEnabled(false); } if (handler.GetInventoryEnabled() && (protocolversion < MC110Version || protocolversion > MC1165Version)) { - Translations.WriteLineFormatted("extra.inventory_disabled"); + log.Error(Translations.Get("extra.inventory_disabled")); handler.SetInventoryEnabled(false); } if (handler.GetEntityHandlingEnabled() && (protocolversion < MC110Version || protocolversion > MC1165Version)) { - Translations.WriteLineFormatted("extra.entity_disabled"); + log.Error(Translations.Get("extra.entity_disabled")); handler.SetEntityHandlingEnabled(false); } @@ -1189,12 +1191,12 @@ namespace MinecraftClient.Protocol.Handlers } else if (packetID == 0x02) //Login successful { - Translations.WriteLineFormatted("mcc.server_offline"); + log.Info(Translations.Get("mcc.server_offline")); login_phase = false; if (!pForge.CompleteForgeHandshake()) { - Translations.WriteLineFormatted("error.forge"); + log.Error(Translations.Get("error.forge")); return false; } @@ -1214,12 +1216,11 @@ namespace MinecraftClient.Protocol.Handlers System.Security.Cryptography.RSACryptoServiceProvider RSAService = CryptoHandler.DecodeRSAPublicKey(serverKey); byte[] secretKey = CryptoHandler.GenerateAESPrivateKey(); - if (Settings.DebugMessages) - Translations.WriteLineFormatted("debug.crypto"); + log.Debug(Translations.Get("debug.crypto")); if (serverIDhash != "-") { - Translations.WriteLine("mcc.session"); + log.Info(Translations.Get("mcc.session")); if (!ProtocolHandler.SessionCheck(uuid, sessionID, CryptoHandler.getServerHash(serverIDhash, serverKey, secretKey))) { handler.OnConnectionLost(ChatBot.DisconnectReason.LoginRejected, Translations.Get("mcc.session_fail")); @@ -1254,7 +1255,7 @@ namespace MinecraftClient.Protocol.Handlers if (!pForge.CompleteForgeHandshake()) { - Translations.WriteLineFormatted("error.forge_encrypt"); + log.Error(Translations.Get("error.forge_encrypt")); return false; } diff --git a/MinecraftClient/Protocol/IMinecraftComHandler.cs b/MinecraftClient/Protocol/IMinecraftComHandler.cs index 9c3f57d4..5c044ad9 100644 --- a/MinecraftClient/Protocol/IMinecraftComHandler.cs +++ b/MinecraftClient/Protocol/IMinecraftComHandler.cs @@ -37,6 +37,7 @@ namespace MinecraftClient.Protocol void SetNetworkPacketCaptureEnabled(bool enabled); int GetProtocolVersion(); Container GetInventory(int inventoryID); + ILogger GetLogger(); /// /// Called when a network packet received or sent diff --git a/MinecraftClient/Resources/config/MinecraftClient.ini b/MinecraftClient/Resources/config/MinecraftClient.ini index 7e3b1e7e..7e5a071d 100644 --- a/MinecraftClient/Resources/config/MinecraftClient.ini +++ b/MinecraftClient/Resources/config/MinecraftClient.ini @@ -35,11 +35,21 @@ accountlist=accounts.txt # See README > 'Servers and Accounts file' fo serverlist=servers.txt # See README > 'Servers and Accounts file' for more info about this file playerheadicon=true # Only works on Windows XP-8 or Windows 10 with old console exitonfailure=false # Disable pauses on error, for using MCC in non-interactive scripts -debugmessages=false # Please enable this before submitting bug reports. Thanks! scriptcache=true # Cache compiled scripts for faster load on low-end devices timestamps=false # Prepend timestamps to chat messages autorespawn=false # Toggle auto respawn if client player was dead (make sure your spawn point is safe) +[Logging] +# Only affect the messages on console. +debugmessages=false # Please enable this before submitting bug reports. Thanks! +chatmessages=true # Show server chat messages +warningmessages=true # Show warning messages +errormessages=true # Show error messages +infomessages=true # Informative messages (i.e Most of the message from MCC) +#chatfilter= # Regex for filtering chat message +#debugfilter= # Regex for filtering debug message +filtermode=blacklist # blacklist OR whitelist. Blacklist hide message match regex. Whitelist show message match regex + [AppVars] # yourvar=yourvalue # can be used in some other fields as %yourvar% diff --git a/MinecraftClient/Settings.cs b/MinecraftClient/Settings.cs index 40aaf572..5cd0c695 100644 --- a/MinecraftClient/Settings.cs +++ b/MinecraftClient/Settings.cs @@ -94,12 +94,22 @@ namespace MinecraftClient public static bool InventoryHandling = false; public static string PrivateMsgsCmdName = "tell"; public static CacheType SessionCaching = CacheType.Disk; - public static bool DebugMessages = false; public static bool ResolveSrvRecords = true; public static bool ResolveSrvRecordsShortTimeout = true; public static bool EntityHandling = false; public static bool AutoRespawn = false; + // Logging + public enum FilterModeEnum { Blacklist, Whitelist } + public static bool DebugMessages = false; + public static bool ChatMessages = true; + public static bool InfoMessages = true; + public static bool WarningMessages = true; + public static bool ErrorMessages = true; + public static Regex ChatFilter = null; + public static Regex DebugFilter = null; + public static FilterModeEnum FilterMode = FilterModeEnum.Blacklist; + //AntiAFK Settings public static bool AntiAFK_Enabled = false; public static int AntiAFK_Delay = 600; @@ -197,7 +207,7 @@ namespace MinecraftClient private static readonly Dictionary> Servers = new Dictionary>(); - private enum ParseMode { Default, Main, AppVars, Proxy, MCSettings, AntiAFK, Hangman, Alerts, ChatLog, AutoRelog, ScriptScheduler, RemoteControl, ChatFormat, AutoRespond, AutoAttack, AutoFishing, AutoEat, AutoCraft, AutoDrop, Mailer, ReplayMod }; + private enum ParseMode { Default, Main, AppVars, Proxy, MCSettings, AntiAFK, Hangman, Alerts, ChatLog, AutoRelog, ScriptScheduler, RemoteControl, ChatFormat, AutoRespond, AutoAttack, AutoFishing, AutoEat, AutoCraft, AutoDrop, Mailer, ReplayMod, Logging }; /// @@ -246,6 +256,7 @@ namespace MinecraftClient case "mailer": pMode = ParseMode.Mailer; break; case "autodrop": pMode = ParseMode.AutoDrop; break; case "replaymod": pMode = ParseMode.ReplayMod; break; + case "logging": pMode = ParseMode.Logging; break; default: pMode = ParseMode.Default; break; } @@ -285,8 +296,9 @@ namespace MinecraftClient case "enableentityhandling": EntityHandling = str2bool(argValue); break; case "inventoryhandling": InventoryHandling = str2bool(argValue); break; case "privatemsgscmdname": PrivateMsgsCmdName = argValue.ToLower().Trim(); break; - case "debugmessages": DebugMessages = str2bool(argValue); break; case "autorespawn": AutoRespawn = str2bool(argValue); break; + // Backward compatible so people can still enable debug with old config format + case "debugmessages": DebugMessages = str2bool(argValue); break; case "botowners": Bots_Owners.Clear(); @@ -396,6 +408,25 @@ namespace MinecraftClient } break; + case ParseMode.Logging: + switch (argName.ToLower()) + { + case "debugmessages": DebugMessages = str2bool(argValue); break; + case "chatmessages": ChatMessages = str2bool(argValue); break; + case "warningmessages": WarningMessages = str2bool(argValue); break; + case "errormessages": ErrorMessages = str2bool(argValue); break; + case "infomessages": InfoMessages = str2bool(argValue); break; + case "chatfilter": ChatFilter = new Regex(argValue); break; + case "debugfilter": DebugFilter = new Regex(argValue); break; + case "filtermode": + if (argValue.ToLower().StartsWith("white")) + FilterMode = FilterModeEnum.Whitelist; + else + FilterMode = FilterModeEnum.Blacklist; + break; + } + break; + case ParseMode.Alerts: switch (argName.ToLower()) {